libro / blog / express-rutas-parametros

// express

Tu primer servidor con Express: rutas, parámetros y query strings

Gustavo Arias9 min de lectura

TL;DR Express añade a Node.js un sistema de rutas y middleware. Lee los datos de la URL con req.params (/posts/:id) y req.query (?page=2), el cuerpo con express.json() y req.body, organiza las rutas en Routers por recurso y responde siempre con el código de estado adecuado.

Con el módulo http de Node.js se puede construir un servidor, pero en cuanto tenés más de tres rutas el código se vuelve una cadena de if sobre req.url. Express resuelve eso con un sistema de rutas y middleware simple y flexible, y por eso es el framework más usado del ecosistema. En el libro Node.js desde cero es el centro de la segunda parte; acá tenés tu primer servidor, paso a paso.

Instalar Express y el primer servidor

$ mkdir api-blog && cd api-blog
$ npm init -y
$ npm pkg set type=module     # usar import/export
$ npm install express
import express from 'express';

const app = express();
const PORT = process.env.PORT ?? 3000;

app.get('/', (req, res) => {
  res.send('¡Hola desde Express!');
});

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

Ejecutalo con node --watch src/server.js: con --watch, Node.js reinicia el servidor cada vez que guardás un cambio.

Rutas y métodos HTTP

Una ruta es la combinación de un método HTTP y una ruta. En una API, el método indica la acción y la ruta, el recurso:

Método y rutaAcciónCódigo de éxito
GET /postsListar posts200
GET /posts/:idVer un post200 (o 404)
POST /postsCrear un post201
PUT /posts/:idReemplazar un post200
DELETE /posts/:idBorrar un post204

Parámetros de ruta y query strings

import express from 'express';

const app = express();
app.use(express.json());   // lee cuerpos JSON y los deja en req.body

// Datos en memoria para el ejemplo (en el libro se conectan a una base de datos)
const posts = [
  { id: 1, titulo: 'Hola Node.js', publicado: true },
  { id: 2, titulo: 'El event loop', publicado: true },
  { id: 3, titulo: 'Borrador sobre streams', publicado: false },
];

// GET /posts?publicado=true&page=1&limit=10
app.get('/posts', (req, res) => {
  const { publicado, page = '1', limit = '10' } = req.query;

  let resultado = posts;
  if (publicado !== undefined) {
    resultado = resultado.filter((p) => String(p.publicado) === publicado);
  }

  const pagina = Math.max(1, Number.parseInt(page, 10) || 1);
  const limite = Math.min(50, Number.parseInt(limit, 10) || 10);   // tope para no devolver todo
  const datos = resultado.slice((pagina - 1) * limite, pagina * limite);

  res.json({ pagina, total: resultado.length, datos });
});

// GET /posts/2
app.get('/posts/:id', (req, res) => {
  const post = posts.find((p) => p.id === Number(req.params.id));
  if (!post) {
    return res.status(404).json({ error: 'Post no encontrado' });
  }
  res.json(post);
});

app.listen(3000);
Todo lo que llega en req.params y req.query es texto: por eso se convierte con Number() antes de comparar con un id numérico. Y como lo controla el cliente, hay que validarlo (lo vemos en validación y manejo de errores).

Leer el cuerpo de la petición

// POST /posts  con cuerpo { "titulo": "Middleware en Express" }
app.post('/posts', (req, res) => {
  const { titulo } = req.body ?? {};
  if (typeof titulo !== 'string' || titulo.trim() === '') {
    return res.status(400).json({ error: 'El título es obligatorio' });
  }

  const post = { id: posts.length + 1, titulo: titulo.trim(), publicado: false };
  posts.push(post);

  res.status(201).location(`/posts/${post.id}`).json(post);
});

Sin app.use(express.json()), req.body llega vacío. Para formularios HTML clásicos se usa express.urlencoded(). Podés probar la ruta desde la terminal:

$ curl -X POST http://localhost:3000/posts \
       -H "Content-Type: application/json" \
       -d '{"titulo": "Middleware en Express"}'

Organizar las rutas con Router

Cuando la API crece, conviene separar las rutas de cada recurso en su propio archivo:

import { Router } from 'express';

const router = Router();

router.get('/', (req, res) => { /* listar */ res.json([]); });
router.get('/:id', (req, res) => { /* ver uno */ res.json({ id: req.params.id }); });
router.post('/', (req, res) => { /* crear */ res.status(201).json(req.body); });

export default router;
import express from 'express';
import postsRouter from './routes/posts.js';

const app = express();
app.use(express.json());
app.use('/api/posts', postsRouter);   // todas sus rutas cuelgan de /api/posts

export default app;

Separar app.js (la configuración) de server.js (el listen) tiene una ventaja extra: podés importar la app en los tests sin arrancar el servidor.

Errores comunes

De Express básico a una API completa

El libro Node.js desde cero introduce Express.js y sus rutas, y lo lleva hasta una API REST completa para un blog en el primer proyecto.

Comprar en Amazon