Ejercicio: Proyecto: API de tareas
Objetivo
Construir en tu computadora la API de tareas completa y reproducir en JavaScript puro sus cuatro piezas centrales: validación, filtros, paginación y almacén.
Cómo se trabaja
La parte A es el proyecto real: sigue los pasos de la página del proyecto en tu terminal. La parte B comprueba en el navegador las cuatro piezas que lo sostienen. Pega solo el JavaScript (sin import, export ni <script>): las cuatro funciones juntas en el comprobador.
Pasos
Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.
💡 Pista 1: el servidor no arranca
Lee el mensaje: Cannot find module suele ser una ruta de import mal escrita (en ES Modules hay que poner la extensión .js).
💡 Pista 2: paginar
Convierte con Number y Math.floor. Una condición como !(p >= 1) también atrapa NaN.
💡 Pista 3: crearAlmacen
Es una función que devuelve un objeto con listar, crear, actualizar y borrar, todas async. cargar y guardar son auxiliares internas.
🔎 Comprueba tu trabajo
Pega tu código y pulsa Comprobar. La revisión ocurre en tu navegador; nada se envía a ningún sitio. Cada punto en rojo incluye una pista. Pega solo el JavaScript, sin las etiquetas <script>.
✅ Ver una solución posible
Intenta resolverlo antes de mirar. Tu versión puede ser distinta y también correcta.
function validarTarea(datos, parcial = false) {
const errores = [];
const t = datos?.titulo;
if (t === undefined && parcial) {
// en modo parcial el título es opcional
} else if (typeof t !== "string" || t.trim() === "") {
errores.push("titulo obligatorio");
} else if (t.length > 100) {
errores.push("titulo demasiado largo");
}
if (datos?.hecha !== undefined && typeof datos.hecha !== "boolean") {
errores.push("hecha debe ser booleano");
}
if (datos?.prioridad !== undefined && !["baja", "media", "alta"].includes(datos.prioridad)) {
errores.push("prioridad invalida");
}
return errores;
}
function filtrarTareas(tareas, query = {}) {
return tareas.filter((t) => {
if (query.hecha !== undefined && t.hecha !== (query.hecha === "true")) return false;
if (query.prioridad !== undefined && t.prioridad !== query.prioridad) return false;
if (query.q && !t.titulo.toLowerCase().includes(String(query.q).toLowerCase())) return false;
return true;
});
}
function paginar(items, pagina, limite) {
let p = Math.floor(Number(pagina));
let l = Math.floor(Number(limite));
if (!(p >= 1)) p = 1;
if (!(l >= 1)) l = 10;
l = Math.min(l, 50);
const inicio = (p - 1) * l;
return {
datos: items.slice(inicio, inicio + l),
total: items.length,
pagina: p,
limite: l,
paginas: Math.max(1, Math.ceil(items.length / l)),
};
}
function crearAlmacen(leer, escribir) {
async function cargar() {
try {
return JSON.parse(await leer());
} catch (error) {
if (error.code === "ENOENT") return [];
throw error;
}
}
async function guardar(tareas) {
await escribir(JSON.stringify(tareas, null, 2));
}
return {
listar: cargar,
async crear(datos) {
const tareas = await cargar();
const id = tareas.reduce((max, t) => Math.max(max, t.id), 0) + 1;
const nueva = { id, ...datos };
tareas.push(nueva);
await guardar(tareas);
return nueva;
},
async actualizar(id, cambios) {
const tareas = await cargar();
const i = tareas.findIndex((t) => t.id === id);
if (i === -1) return null;
tareas[i] = { ...tareas[i], ...cambios, id };
await guardar(tareas);
return tareas[i];
},
async borrar(id) {
const tareas = await cargar();
const i = tareas.findIndex((t) => t.id === id);
if (i === -1) return false;
tareas.splice(i, 1);
await guardar(tareas);
return true;
},
};
}
💡 🏁 Reto extra (opcional)
Añade a filtrarTareas el parámetro orden para ordenar por título (orden=titulo, y orden=-titulo en descendente) y escribe una prueba con node:test.