← Node & Express

Reto de código

construir una API REST CRUD de "tareas" con un array en memoria.

Reto: construir una API REST CRUD de "tareas" con un array en memoria.
Debe soportar GET (listar y uno), POST (crear), PUT (actualizar) y DELETE (borrar),
devolviendo los status codes correctos.

Entrada / salida esperada (ejemplos):
  GET    /tareas        -> 200 [ ...lista... ]
  GET    /tareas/1      -> 200 { id: 1, ... }   o  404 si no existe
  POST   /tareas        -> 201 { id, titulo, hecha }   o  400 si falta titulo
  PUT    /tareas/1      -> 200 { ...actualizada... }   o  404 si no existe
  DELETE /tareas/1      -> 204 sin cuerpo               o  404 si no existe

Como probarlo:
  1. npm install
  2. node 06-retos-codigo/crud-in-memory.js
  3. curl http://localhost:3000/tareas
     curl -X POST http://localhost:3000/tareas -H "Content-Type: application/json" -d '{"titulo":"Estudiar"}'
Ver solución
// Reto: construir una API REST CRUD de "tareas" con un array en memoria.
// Debe soportar GET (listar y uno), POST (crear), PUT (actualizar) y DELETE (borrar),
// devolviendo los status codes correctos.
//
// Entrada / salida esperada (ejemplos):
//   GET    /tareas        -> 200 [ ...lista... ]
//   GET    /tareas/1      -> 200 { id: 1, ... }   o  404 si no existe
//   POST   /tareas        -> 201 { id, titulo, hecha }   o  400 si falta titulo
//   PUT    /tareas/1      -> 200 { ...actualizada... }   o  404 si no existe
//   DELETE /tareas/1      -> 204 sin cuerpo               o  404 si no existe
//
// Como probarlo:
//   1. npm install
//   2. node 06-retos-codigo/crud-in-memory.js
//   3. curl http://localhost:3000/tareas
//      curl -X POST http://localhost:3000/tareas -H "Content-Type: application/json" -d '{"titulo":"Estudiar"}'

const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;

// express.json() es un middleware que parsea el body JSON de la peticion
// y lo deja disponible en req.body. Sin esto, req.body es undefined.
app.use(express.json());

// "Base de datos" en memoria. En un proyecto real seria una base de datos.
let tareas = [
  { id: 1, titulo: "Aprender Express", hecha: false },
  { id: 2, titulo: "Practicar entrevistas", hecha: false },
];
let siguienteId = 3; // para generar ids unicos al crear

// READ (lista): GET /tareas -> 200 con el array completo.
app.get("/tareas", (req, res) => {
  res.status(200).json(tareas);
});

// READ (uno): GET /tareas/:id -> 200 si existe, 404 si no.
app.get("/tareas/:id", (req, res) => {
  // req.params.id llega como string; lo convertimos a numero para comparar.
  const id = Number(req.params.id);
  const tarea = tareas.find((t) => t.id === id);

  if (!tarea) {
    // 404 = recurso no encontrado. Devolvemos un mensaje, nunca un stack trace.
    return res.status(404).json({ error: "Tarea no encontrada" });
  }

  res.status(200).json(tarea);
});

// CREATE: POST /tareas -> 201 (Created) si se creo, 400 si el body es invalido.
app.post("/tareas", (req, res) => {
  const { titulo } = req.body;

  // Validamos la entrada antes de tocar los datos.
  if (!titulo || typeof titulo !== "string") {
    return res.status(400).json({ error: "El campo 'titulo' es obligatorio" });
  }

  const nueva = { id: siguienteId++, titulo, hecha: false };
  tareas.push(nueva);

  // 201 Created es el status correcto al crear un recurso nuevo.
  res.status(201).json(nueva);
});

// UPDATE: PUT /tareas/:id -> 200 si se actualizo, 404 si no existe.
app.put("/tareas/:id", (req, res) => {
  const id = Number(req.params.id);
  const tarea = tareas.find((t) => t.id === id);

  if (!tarea) {
    return res.status(404).json({ error: "Tarea no encontrada" });
  }

  // Actualizamos solo los campos que vengan en el body.
  if (req.body.titulo !== undefined) tarea.titulo = req.body.titulo;
  if (req.body.hecha !== undefined) tarea.hecha = req.body.hecha;

  res.status(200).json(tarea);
});

// DELETE: DELETE /tareas/:id -> 204 (sin cuerpo) si se borro, 404 si no existe.
app.delete("/tareas/:id", (req, res) => {
  const id = Number(req.params.id);
  const existe = tareas.some((t) => t.id === id);

  if (!existe) {
    return res.status(404).json({ error: "Tarea no encontrada" });
  }

  tareas = tareas.filter((t) => t.id !== id);

  // 204 No Content: la operacion tuvo exito pero no hay nada que devolver.
  res.status(204).end();
});

app.listen(PORT, () => {
  console.log(`API CRUD de tareas en http://localhost:${PORT}`);
});

module.exports = app;