FullStackJS Camp
Módulo 6·practica·1h
Objetivos de aprendizaje
  • Organizar rutas con express.Router()
  • Implementar una ruta genérica 404 con app.use()
  • Definir un contrato de respuesta JSON consistente
  • Retornar los códigos HTTP adecuados en cada situación

Rutas y Respuestas Estándar

Una API bien diseñada comunica claramente su estado a través de los códigos HTTP y mantiene un contrato de respuesta consistente que los clientes pueden anticipar.

Códigos HTTP más importantes

CódigoNombreCuándo usarlo
200OKGET exitoso
201CreatedPOST que crea un recurso
204No ContentDELETE exitoso (sin body)
400Bad RequestDatos de entrada inválidos
401UnauthorizedNo autenticado
403ForbiddenAutenticado pero sin permisos
404Not FoundRecurso no existe
409ConflictDuplicado (ej: email ya existe)
500Internal Server ErrorError inesperado del servidor

Contrato de respuesta consistente

Define un formato fijo para todas tus respuestas:

javascript
// utils/respuesta.js

// Respuesta de éxito
export function ok(res, datos, status = 200) {
  return res.status(status).json({
    ok: true,
    datos,
  });
}

// Respuesta de error
export function error(res, mensaje, status = 400) {
  return res.status(status).json({
    ok: false,
    error: mensaje,
  });
}

// Recurso no encontrado
export function notFound(res, recurso = "Recurso") {
  return res.status(404).json({
    ok: false,
    error: `${recurso} no encontrado`,
  });
}

Uso en rutas

javascript
import express from "express";
import { ok, error, notFound } from "./utils/respuesta.js";

const app = express();
app.use(express.json());

const productos = [
  { id: 1, nombre: "Laptop", precio: 999 },
  { id: 2, nombre: "Mouse", precio: 29 },
];

// ✅ GET todos → 200 + array
app.get("/api/productos", (req, res) => {
  ok(res, productos);
});

// ✅ GET por ID → 200 o 404
app.get("/api/productos/:id", (req, res) => {
  const producto = productos.find((p) => p.id === Number(req.params.id));
  if (!producto) return notFound(res, "Producto");
  ok(res, producto);
});

// ✅ POST crear → 201 o 400
app.post("/api/productos", (req, res) => {
  const { nombre, precio } = req.body;

  if (!nombre || precio === undefined) {
    return error(res, "Los campos nombre y precio son requeridos");
  }
  if (typeof precio !== "number" || precio < 0) {
    return error(res, "El precio debe ser un número positivo");
  }

  const nuevo = { id: Date.now(), nombre, precio };
  productos.push(nuevo);
  ok(res, nuevo, 201);
});

// ✅ PUT actualizar → 200 o 404
app.put("/api/productos/:id", (req, res) => {
  const idx = productos.findIndex((p) => p.id === Number(req.params.id));
  if (idx === -1) return notFound(res, "Producto");

  productos[idx] = { ...productos[idx], ...req.body };
  ok(res, productos[idx]);
});

// ✅ DELETE → 204 o 404
app.delete("/api/productos/:id", (req, res) => {
  const idx = productos.findIndex((p) => p.id === Number(req.params.id));
  if (idx === -1) return notFound(res, "Producto");

  productos.splice(idx, 1);
  res.status(204).send(); // 204 = sin cuerpo de respuesta
});

Ruta genérica 404

La ruta 404 siempre va al final, después de todas las demás rutas. Es un app.use() sin path:

javascript
// ❌ Mal — antes de las rutas, nunca llegamos a ellas
app.use((req, res) => {
  res.status(404).json({ error: "No encontrado" });
});

app.get("/usuarios", ...);  // Esta ruta nunca se alcanza


// ✅ Bien — al final de todo
app.get("/api/productos", ...);
app.post("/api/productos", ...);
// ... todas las rutas ...

// Ruta genérica 404 — siempre la última
app.use((req, res) => {
  res.status(404).json({
    ok: false,
    error: `La ruta ${req.method} ${req.url} no existe en esta API`,
  });
});

Organizar rutas con Router

Cuando tu app crece, usa express.Router() para separar rutas en archivos:

javascript
// routes/productos.js
import { Router } from "express";
import { ok, error, notFound } from "../utils/respuesta.js";

const router = Router();
const productos = [];

router.get("/", (req, res) => ok(res, productos));
router.get("/:id", (req, res) => {
  const p = productos.find((p) => p.id === Number(req.params.id));
  if (!p) return notFound(res, "Producto");
  ok(res, p);
});
router.post("/", (req, res) => {
  /* ... */
});

export default router;
javascript
// server.js
import express from "express";
import productosRouter from "./routes/productos.js";
import usuariosRouter from "./routes/usuarios.js";

const app = express();
app.use(express.json());

// Montar routers con prefijo
app.use("/api/productos", productosRouter);
app.use("/api/usuarios", usuariosRouter);

// 404 genérico
app.use((req, res) => {
  res.status(404).json({ ok: false, error: `Ruta no encontrada: ${req.url}` });
});

app.listen(3000);

Estructura de proyecto recomendada

code
mi-api/
├── server.js              ← Punto de entrada
├── package.json
├── .env
├── routes/
│   ├── productos.js
│   └── usuarios.js
├── middlewares/
│   ├── logger.js
│   └── auth.js
└── utils/
    └── respuesta.js