Inicio / Artículos / Identificar versus dar forma: elegir parámetros de ruta o cadenas de consulta en Express

Identificar versus dar forma: elegir parámetros de ruta o cadenas de consulta en Express

Aprenda cuándo un valor debe utilizarse como parámetro de una ruta en Express y cuándo como cadena de consulta, cómo leer req.params y req.query, y cómo manejar valores por defecto y tipos de manera segura.

1324 palabras

Tome la URL /users/42?sort=name&order=asc. El 42 selecciona a un usuario en particular; sort=name&order=asc solo modifica la forma en que se organiza la respuesta. Confundir estas dos funciones, por ejemplo tratando un ID como filtro o un filtro como ID, es una causa común de rutas Express problemáticas. Después de leer esta guía tendrá una prueba sencilla para determinar a dónde pertenece un valor, y sabrá cómo Express expone cada tipo de parámetro y qué sorpresas esperar de ellos.

Los parámetros de ruta identifican un recurso

Un parámetro de ruta es un segmento con nombre que forma parte del patrón de la ruta. Indica al servidor sobre qué recurso específico se refiere la solicitud.

/users/:id

El dos puntos marca :id como un marcador de posición. Cuando llega una solicitud a /users/42, Express coincide con el patrón y registra 42 como el valor de id. La misma lógica se aplica a cualquier recurso que tenga una identidad:

/users/42        → which user
/products/17      → which product
/orders/1042      → which order

Cada uno de estos caminos identifica exactamente una cosa. Sin ese segmento, la pregunta “¿cuál?” no tiene respuesta.

Las cadenas de consulta dan forma a la respuesta

Una cadena de consulta es todo lo que está después del ?: una lista de pares key=value unidos por &. No selecciona un recurso; en su lugar filtra, ordena, pagina o modifica de otra manera lo que se devuelve.

/users?sort=name&order=asc

Aquí sort y order dejan el recurso (la colección de usuarios) sin cambios y solo afectan su presentación. Ejemplos más típicos:

/products?category=electronics&maxPrice=500
/search?q=laptop&page=2

Una prueba con una pregunta para distinguirlos

Pregunte qué sucede si se elimina el valor:

  • Si la ruta deja de tener sentido (no se puede obtener “un usuario” sin especificar cuál), el valor es un identificador y debe formar parte de la ruta.
  • Si la ruta sigue funcionando y simplemente devuelve el resultado predeterminado, sin filtrar (todos los usuarios, en el orden estándar), el valor es un modificador y debe incluirse en la cadena de consulta.

Esta prueba también sugiere cómo manejar errores. Un recurso faltante detrás de un parámetro de ruta generalmente debería generar un 404, mientras que un filtro que no coincide con nada normalmente debería devolver un 200 con una lista vacía. Para conocer más sobre el diseño de URLs orientado a recursos, consulte REST APIs para principiantes.

Lectura de parámetros de ruta con req.params

Los parámetros se declaran con un dos puntos en la ruta, y sus valores capturados aparecen en req.params con los mismos nombres:

app.get("/users/:id", (req, res) => {
  const userId = req.params.id;
  res.send(`Fetching user with ID: ${userId}`);
});

Una solicitud a /users/42 establece req.params.id como la cadena "42".

Varios parámetros en una misma ruta

Los recursos anidados simplemente declaran más marcadores de posición:

app.get("/users/:userId/orders/:orderId", (req, res) => {
  const { userId, orderId } = req.params;
  res.send(`User ${userId}, Order ${orderId}`);
});

Para /users/42/orders/1042, el desestructurado devuelve userId igual a "42" y orderId igual a "1042". Los nombres de los parámetros deben ser únicos dentro de una ruta y deberían describir lo que identifican; userId y orderId son mucho más claros que dos id anónimos.

Lectura de cadenas de consulta con req.query

Los valores de la consulta no necesitan declaración en la ruta. Express analiza todo lo que sigue al ? y lo coloca en req.query:

app.get("/users", (req, res) => {
  const { sort, order } = req.query;
  res.send(`Sorting by ${sort}, order: ${order}`);
});

Para /users?sort=name&order=asc, se obtiene req.query.sort con el valor "name" y req.query.order con el valor "asc". La ruta en sí sigue siendo /users, por lo que el mismo manejador sirve tanto la solicitud sin filtrado como la solicitada con filtrado.

Proporcionar valores por defecto para campos opcionales

Dado que los clientes a menudo omiten valores de consulta, los manejadores suelen recurrir a valores por defecto razonables:

app.get("/products", (req, res) => {
  const sort = req.query.sort || "default";
  const page = req.query.page || 1;
  res.send(`Sorting: ${sort}, Page: ${page}`);
});

Una solicitud simple a /products sigue funcionando, utilizando los valores por defecto. Hay que tener en cuenta una sutileza: cuando el cliente envía page=2, page es la cadena "2", pero cuando se omite, es el número 1. Este tipo de mezcla de tipos causa errores posteriormente (por ejemplo, concatenación de cadenas en lugar de suma). Convierta explícitamente los valores, como con Number(req.query.page) || 1, y valide el resultado antes de usarlo en una consulta a la base de datos.

Los valores no siempre son cadenas simples

Un parámetro repetido en la URL, como ?tag=a&tag=b, llega como un array en lugar de una cadena. Dependiendo de la configuración del analizador de consultas, la sintaxis con corchetes también puede generar objetos anidados. Express 5 cambió el analizador predeterminado por uno más simple que el utilizado en Express 4, así que consulte la documentación de su versión si depende de objetos de consulta anidados. En cualquier caso, nunca asuma el tipo de un valor de consulta; trate req.query como entrada no fiable.

Elegir qué tipo necesita una ruta

Parámetros de ruta para un recurso específico

app.get("/users/:id", ...)          // one specific user
app.get("/products/:id", ...)       // one specific product
app.get("/orders/:orderId", ...)    // one specific order

Cada una de estas rutas se refiere a un elemento concreto y único. Si la ruta no tiene sentido sin ese valor, inclúyalo en la ruta.

Cadenas de consulta para filtrado, ordenamiento y paginación

app.get("/users", ...)     // ?role=admin&status=active
app.get("/products", ...)  // ?category=electronics&maxPrice=500&sort=price
app.get("/search", ...)    // ?q=laptop&page=2

Cada uno de estos sigue teniendo sentido sin ninguna consulta: “todos los usuarios”, “todos los productos” o una página de búsqueda vacía. Los valores que simplemente restringen o ordenan los resultados son modificadores opcionales y deben ir después del ?.

Combinar ambos en una única ruta

Los endpoints reales suelen utilizar ambos al mismo tiempo. El parámetro selecciona al propietario, mientras que la consulta restringe los datos relacionados:

app.get("/users/:id/orders", (req, res) => {
  const userId = req.params.id;         // which user
  const status = req.query.status;      // optional filter: only their pending orders, for example

  res.send(`Orders for user ${userId}, filtered by status: ${status || "all"}`);
});

Una solicitud a /users/42/orders?status=pending se entiende de forma natural: 42 indica a cuáles órdenes se refiere, y status=pending indica cuáles de esas órdenes incluir. Cuando falta status, el procesador muestra “todos”, lo que coincide con la idea de que la ausencia de un modificador significa el resultado sin filtrar.

Preguntas comunes

¿Puede una única ruta utilizar ambos?

Sí, y es muy común. El ejemplo combinado anterior representa el patrón típico: un identificador para el recurso padre más filtros opcionales para sus hijos.

¿Los parámetros son siempre obligatorios y los valores de consulta siempre opcionales?

Esa es una convención fuerte, no una regla estricta. Express sí admite segmentos de ruta opcionales, y nada impide que una API exija un valor de consulta. Aun así, rige la pauta práctica de que los identificadores obligatorios se incluyan en la ruta y los modificadores opcionales en la consulta con valores por defecto.

¿Es req.params.id un número?

No. Todo lo que se extrae de una URL es una cadena de texto, incluso cuando parece numérico. Conviértalo explícitamente, por ejemplo con Number(req.params.id), y rechaza los valores que den como resultado NaN antes de acceder a la base de datos.

¿Qué pasa si falta un valor de consulta esperado?

La clave simplemente no está presente en req.query, por lo que al leerla se obtiene undefined. Esa es precisamente la razón por la cual los valores predeterminados de respaldo mostrados anteriormente son una práctica estándar.

Conclusión

Ambos tipos de valores se encuentran en la misma URL, pero cumplen funciones diferentes. Los parámetros de ruta, que se leen desde req.params, identifican el elemento específico al que se refiere una solicitud. Las cadenas de consulta, que se leen desde req.query, determinan cómo se filtrará, ordenará o paginará la respuesta, y deben venir con valores predeterminados. Trátalos ambos como cadenas sin tipo provenientes del mundo exterior: conviértelas y validélas antes de usarlas, y el diseño de tus rutas seguirá siendo predecible a medida que la API crezca.

Lecturas relacionadas

  • De la subida a la URL: Almacenamiento y servicio seguro de archivos de usuario en Express — Aprenda dónde deben guardar las aplicaciones Express los archivos subidos, cómo express.static asocia una carpeta con URLs y qué medidas de seguridad evitan que las subidas de los usuarios se conviertan en vulnerabilidades de seguridad.