Filtrado y ordenamiento: consultas expresivas sin romper la simplicidad REST

Capítulo 6

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

Objetivo: consultas expresivas sin perder consistencia

En colecciones REST, el filtrado y el ordenamiento suelen crecer “orgánicamente” hasta volverse inconsistentes: cada endpoint inventa sus propios parámetros, aparecen combinaciones ambiguas y el backend termina aceptando cualquier cosa. La meta es definir un esquema único y predecible para: filtrar (qué registros), ordenar (en qué orden), seleccionar campos (qué atributos devolver) y expandir relaciones (qué datos relacionados incluir), manteniendo reglas claras de validación y límites para evitar respuestas excesivas.

Esquema recomendado de parámetros de consulta

Propón un conjunto pequeño y consistente de parámetros, reutilizable en todas las colecciones:

  • status=active (filtros simples por igualdad)
  • createdAt[gte]=2025-01-01T00:00:00Z (operadores explícitos)
  • q=texto (búsqueda libre controlada)
  • sort=field,-otherField (ordenamiento)
  • fields=id,name,status (selección de campos)
  • expand=customer,items.product (expansión controlada)

Regla de oro: un mismo significado, un mismo parámetro. Evita variantes como orderBy en un endpoint y sortBy en otro, o search vs q.

Filtrado: operadores comunes y sintaxis

1) Igualdad simple (forma corta)

Para el caso más frecuente, permite field=value como atajo de igualdad:

GET /orders?status=active

Esto equivale a status[eq]=active. Mantener el atajo mejora la ergonomía sin perder formalidad.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

2) Operadores explícitos (forma extendida)

Para comparaciones y casos no triviales, usa la forma field[op]=value. Operadores recomendados:

OperadorSignificadoEjemplo
eqigualstatus[eq]=active
neqdistintostatus[neq]=archived
ltmenor quetotal[lt]=100
ltemenor o igualtotal[lte]=100
gtmayor quetotal[gt]=100
gtemayor o igualcreatedAt[gte]=2025-01-01T00:00:00Z
inpertenece a un conjuntostatus[in]=active,pending
containscontiene (texto/colección)name[contains]=pro

Recomendación: documenta si contains es sensible a mayúsculas, si usa normalización (acentos) y si aplica a texto, arrays o ambos.

3) Búsqueda libre con q (controlada)

q es útil para una búsqueda “tipo caja de búsqueda” sin exponer la complejidad del motor (SQL full-text, Elastic, etc.). Mantén q como un contrato funcional, no como un lenguaje.

GET /customers?q=juan perez

Buenas prácticas para q:

  • Define qué campos participa (p. ej., name, email) y si hay ponderación.
  • Limita longitud (p. ej., 200 caracteres) y caracteres permitidos.
  • No aceptes sintaxis avanzada (AND/OR, wildcards) salvo que la estandarices y valides estrictamente.

Reglas de validación (para evitar “filtros sorpresa”)

Un esquema consistente requiere validación explícita. Define una lista blanca por recurso:

  • Campos filtrables: qué atributos aceptan filtros.
  • Operadores permitidos por campo: no todos los operadores aplican a todos los tipos.
  • Tipos y formatos: fechas ISO-8601, números, booleanos, enums.
  • Límites: tamaño máximo de listas en in, longitud de cadenas, cantidad total de filtros.

Matriz de ejemplo: campos vs operadores

CampoTipoOperadores permitidosNotas
statusenumeq, neq, inValidar contra valores conocidos
createdAtdatetimeeq, lt, lte, gt, gteISO-8601 con zona horaria
totalnumbereq, lt, lte, gt, gteRangos coherentes
namestringeq, containsDefinir sensibilidad a mayúsculas

Errores típicos y cómo responder

Cuando el cliente envía filtros inválidos, responde con un error de validación (p. ej., 400) indicando exactamente qué parámetro falló y por qué. Ejemplos de causas:

  • Campo no filtrable: GET /orders?internalNote[contains]=x
  • Operador no permitido: GET /orders?status[gte]=active
  • Formato inválido: createdAt[gte]=ayer
  • Lista in demasiado grande: status[in]=... con cientos de valores

Recomendación práctica: incluye en el error un código interno (p. ej., INVALID_FILTER) y una lista de detalles por parámetro para que el cliente pueda corregir sin ensayo y error.

Guía práctica paso a paso para diseñar filtros consistentes

Paso 1: define el “contrato de filtrado” por recurso

Para cada colección, crea una tabla interna (o en tu especificación) con:

  • Campos filtrables
  • Operadores por campo
  • Tipo y formato
  • Ejemplos válidos
  • Restricciones (máximos, mínimos, longitudes)

Paso 2: estandariza el parseo de parámetros

Implementa un parser único que convierta la query en una estructura normalizada. Ejemplo conceptual:

// Query: ?status=active&createdAt[gte]=2025-01-01T00:00:00Z&total[lt]=100&status[in]=active,pending
filters = [  { field: "status", op: "eq", value: "active" },  { field: "createdAt", op: "gte", value: "2025-01-01T00:00:00Z" },  { field: "total", op: "lt", value: 100 },  { field: "status", op: "in", value: ["active", "pending"] }]

Decide una regla para conflictos: por ejemplo, si llega status=active y status[in]=active,pending, ¿se combinan con AND, se rechaza o gana uno? Lo más seguro es rechazar ambigüedad con un error claro.

Paso 3: valida antes de ejecutar

Valida: campo permitido, operador permitido, tipo, formato, límites. No delegues esto al motor de base de datos.

Paso 4: traduce a la capa de persistencia

Traduce la estructura normalizada a SQL/ORM/Elastic con mapeos explícitos. Evita concatenar strings de query para prevenir inyecciones y comportamientos inesperados.

Ordenamiento: sort=field,-otherField

Define un parámetro único sort con una lista separada por comas. Un prefijo - indica descendente; sin prefijo, ascendente.

GET /orders?sort=createdAt,-total

Interpretación:

  • createdAt ascendente
  • total descendente

Lista blanca de campos ordenables

No permitas ordenar por cualquier campo. Mantén un conjunto de campos ordenables por recurso (y, si aplica, por rol). Si el cliente envía un campo no permitido:

  • Opción recomendada: rechazar con error de validación (evita resultados “silenciosamente distintos”).
  • Alternativa: ignorar campos inválidos, pero documenta y devuelve advertencias (menos ideal).

Reglas adicionales

  • Define un orden por defecto estable (p. ej., sort=-createdAt), para evitar resultados que “saltan” entre llamadas.
  • Limita la cantidad de campos en sort (p. ej., máximo 3) para proteger rendimiento.
  • Si ordenas por campos nullable, documenta dónde caen los nulls (primero/último) o estandariza el comportamiento.

Selección de campos: fields=... para payloads más pequeños

fields permite que el cliente pida solo ciertos atributos del recurso, reduciendo ancho de banda y costo de serialización.

GET /customers?fields=id,name,status

Reglas recomendadas

  • Lista blanca de campos seleccionables (por seguridad y consistencia).
  • Define si fields es una lista inclusiva (lo usual) o exclusiva (evítalo).
  • Define campos mínimos obligatorios (p. ej., id) aunque no se pidan, si tu contrato lo requiere.
  • Limita la cantidad de campos solicitables (p. ej., máximo 30) para evitar respuestas enormes por abuso.

Ejemplo con respuesta más ligera

GET /orders?fields=id,status,total

Devuelve solo esos atributos (y omite el resto), manteniendo el mismo envelope/estructura de respuesta que uses en tu API.

Expansión controlada: expand=... para evitar múltiples llamadas

expand permite incluir recursos relacionados de forma controlada, evitando que el cliente haga muchas solicitudes para construir una vista. La clave es que sea opt-in y con límites.

GET /orders?expand=customer,items.product

Esto podría incluir:

  • customer embebido dentro de cada order
  • en items, cada item con su product

Reglas para que expand no se vuelva incontrolable

  • Lista blanca de expansiones permitidas por recurso (y por rol).
  • Profundidad máxima (p. ej., 2 niveles: items.product sí, items.product.supplier no).
  • Máximo de expansiones por request (p. ej., 3).
  • Campos por defecto en recursos expandidos (idealmente un “resumen”), o permitir fields también para expandidos mediante una convención documentada.
  • Coste controlado: si una expansión dispara joins pesados, considera rechazarla o degradarla (p. ej., solo IDs) según políticas.

Evitar respuestas excesivas (límites combinados)

La combinación de expand + fields + filtros puede generar respuestas muy grandes. Define límites globales:

  • Máximo de expansiones y profundidad (ya mencionado).
  • Máximo de tamaño de respuesta (si tu infraestructura lo permite) o límites de entidades embebidas (p. ej., máximo 50 items expandidos por order).
  • Si el recurso tiene subcolecciones grandes, no las expandas completas: expande solo un resumen o un subconjunto predecible.

Ejemplos completos (combinando todo)

Ejemplo 1: filtrar por estado y rango de fechas, ordenar y seleccionar campos

GET /orders?status[in]=active,pending&createdAt[gte]=2025-01-01T00:00:00Z&createdAt[lt]=2025-02-01T00:00:00Z&sort=-createdAt&fields=id,status,createdAt,total

Ejemplo 2: búsqueda libre con q y expansión limitada

GET /customers?q=ana garcia&sort=name&fields=id,name,email&expand=lastOrder

Asegúrate de que lastOrder sea una expansión explícitamente soportada (no “cualquier relación”).

Ejemplo 3: validación de campo no permitido en sort

GET /orders?sort=-internalMargin

Si internalMargin no está en la lista blanca de ordenamiento, devuelve un error de validación indicando el campo inválido y, si es útil, los campos permitidos.

Checklist de implementación (rápida y accionable)

  • Define por recurso: filterableFields, sortableFields, selectableFields, expandablePaths.
  • Implementa un parser único para field[op]=value y atajo field=value.
  • Valida: campo, operador, tipo, formato, límites (listas, longitudes, cantidad de filtros).
  • Estándar de ordenamiento: sort=field,-other con lista blanca y máximo de campos.
  • Estándar de selección: fields=... con lista blanca, máximos y campos mínimos.
  • Estándar de expansión: expand=... con lista blanca, profundidad y máximos.
  • Documenta ejemplos por endpoint y casos de error típicos para acelerar adopción por clientes.

Ahora responde el ejercicio sobre el contenido:

Al diseñar parámetros de consulta consistentes para colecciones REST, ¿qué práctica ayuda más a evitar ambigüedades y comportamientos inesperados en filtros y ordenamiento?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Un esquema predecible requiere estandarizar parámetros (p. ej., sort, fields, expand y field[op]) y validar con listas blancas. Ante campos, operadores o combinaciones ambiguas, lo más seguro es rechazar con un error claro.

Siguiente capítulo

Idempotencia y seguridad de reintentos en operaciones REST

Arrow Right Icon
Portada de libro electrónico gratuitaDiseño de APIs REST: buenas prácticas, errores comunes y estándares
60%

Diseño de APIs REST: buenas prácticas, errores comunes y estándares

Nuevo curso

10 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.