Saltar al contenido
Curso de Node.js y APIs
Todos los cursos

Ejercicio: Proyecto: API de tareas

  • ⏱ 120 minutos
  • 📶 Nivel: intermedio

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.

▶ Prueba tu código aquí (opcional)
Consola

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.

    proyecto.js
    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.