Express.js
Routing y matching de rutas
Matching de métodos y paths, orden de rutas, routers, parámetros, acciones y diferencias relevantes de Express 5.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
Express.js
Matching de métodos y paths, orden de rutas, routers, parámetros, acciones y diferencias relevantes de Express 5.
Routing conecta una intención HTTP con una implementación. Express evalúa capas en orden y ejecuta aquellas cuyo método y path coinciden.
router.get('/orders/:orderId', getOrderHandler);
router.post('/orders', validateCreateOrder, createOrderHandler);La ruta debe expresar el recurso o acción pública. El nombre del handler es un detalle interno.
Un servidor necesita distinguir operaciones como:
GET /orders
POST /orders
GET /orders/:orderId
PATCH /orders/:orderIdSin router, la aplicación compararía manualmente método y URL. Express delega el parsing del path, captura parámetros y permite componer middleware por operación.
request
↓
¿coincide el path del middleware?
↓
¿coincide el router montado?
↓
¿coinciden método y path de la ruta?
↓
se ejecuta la cadena de handlersLa primera ruta que responde termina el recorrido. Si una ruta coincide y llama next('route'), Express puede saltar a otra definición de ruta compatible.
router.get('/', listOrders);
router.post('/', createOrder);
router.get('/:orderId', getOrder);GET /orders y POST /orders comparten path, pero tienen semántica distinta. app.all() puede registrar una cadena para todos los métodos, aunque debe usarse con cuidado porque puede ocultar contratos.
Express evalúa en orden de registro. Un patrón dinámico puede capturar un path especial:
router.get('/:orderId', getOrder);
router.get('/search', searchOrders); // puede no alcanzarseMejor:
router.get('/search', searchOrders);
router.get('/:orderId', getOrder);Otra solución es validar el formato de orderId, pero la claridad del orden sigue siendo importante.
Express 5 utiliza una sintaxis actualizada de path-to-regexp. Cambios relevantes:
/*splat./, puede utilizarse /{*splat}.? se expresan con braces, por ejemplo /:file{.:ext}.No migres rutas complejas solo leyendo el código. Añade tests con paths válidos, inválidos y fronteras.
const ordersRouter = Router();
ordersRouter.get('/', listOrders);
ordersRouter.get('/:orderId', getOrder);
app.use('/api/v1/orders', ordersRouter);Dentro del router, / representa /api/v1/orders. Esto permite mover el prefijo sin cambiar cada definición.
router.get('/:orderId', (request, response) => {
const { orderId } = request.params;
});Los params son strings no confiables. Matching no valida UUID, existencia ni autorización.
router.patch(
'/:orderId',
authenticate,
authorize('orders:update'),
validate(updateOrderSchema),
updateOrderHandler,
);La ruta documenta el pipeline de la operación. No escondas etapas críticas dentro de imports difíciles de rastrear.
/businesses/:businessId/branches/:branchId/ordersLa jerarquía puede comunicar contexto, pero una profundidad excesiva acopla URLs al modelo interno. Además, debes verificar relaciones: que la branch pertenezca al business.
Un child router necesita { mergeParams: true } para ver params del padre:
const branchesRouter = Router({ mergeParams: true });Una acción explícita puede ser más clara:
POST /orders/:orderId/cancel
POST /orders/:orderId/confirmForzar cualquier transición a PATCH { status: ... } puede permitir estados inválidos y ocultar precondiciones. REST pragmático busca semántica comprensible, no pureza estética.
Agrupa métodos del mismo path:
router
.route('/:orderId')
.get(getOrder)
.patch(updateOrder)
.delete(deleteOrder);Puede mejorar lectura cuando los handlers comparten contexto, pero no debe convertir un archivo en una lista enorme.
Permite ejecutar lógica al encontrar un parámetro:
router.param('orderId', validateOrderIdParam);Es útil para normalización o carga común, pero puede ocultar queries y autorización. Si cada ruta necesita condiciones distintas, una carga automática puede ser costosa o incorrecta.
Ninguna ruta coincidió. Un middleware terminal responde ROUTE_NOT_FOUND.
La ruta coincidió, pero el caso de uso no encontró una entidad visible.
El path existe, pero el método no está permitido. Express no genera automáticamente una respuesta 405 consistente para toda la API. Requiere metadata, diseño explícito o una capa adicional.
const router = Router();
router.get('/search', validateSearchQuery, searchOrders);
router.post('/', authenticate, validateCreateOrder, createOrder);
router.get('/:orderId', validateOrderId, authorizeOrderRead, getOrder);
router.post('/:orderId/cancel', validateOrderId, authorizeOrderUpdate, cancelOrder);El orden coloca paths literales antes del parámetro dinámico. Cada operación muestra sus precondiciones HTTP.
Según configuración, /orders y /orders/ pueden tratarse de forma equivalente. Decide si necesitas routing estricto.
El routing suele ser case-insensitive por defecto. case sensitive routing cambia el comportamiento.
Los segmentos se decodifican. Valores con slash codificado, caracteres inválidos o encoding defectuoso necesitan pruebas.
Puede capturar assets, rutas internas o errores. Colócalo al final y dale una responsabilidad terminal.
Express puede responder HEAD usando una ruta GET si no existe una HEAD explícita. Revisa coste y headers.
/:id antes de /search.Crea una matriz:
| Método | Path | Resultado esperado |
|---|---|---|
| GET | `/orders/search` | search handler |
| GET | `/orders/uuid` | get handler |
| POST | `/orders` | create handler |
| DELETE | `/orders` | 405 o contrato definido |
| GET | `/unknown` | route 404 |
Incluye paths con encoding, slash final y parámetros inválidos.
Rutas muy genéricas reducen archivos pero aumentan ambigüedad. Rutas excesivamente específicas multiplican contratos. El diseño debe reflejar recursos y operaciones que el consumidor entiende.
/search puede entrar en /:orderId?mergeParams?search es un string válido para el parámetro si la ruta dinámica aparece primero.Parámetros de ruta y ownership profundiza cómo convertir una identidad pública en un recurso validado y autorizado.