← Servidor web y API con Node

Sesión 4 · Semana 2

Leer: listar, filtrar y obtener

Hoy · Hoja de ruta

  1. 1. Aprende: Cómo se implementan las lecturas, con filtros, orden y paginación.
  2. 2. Haz: Las dos rutas de lectura de tu recurso, completas.
  3. 3. Comprueba: Un parámetro inválido no rompe nada ni devuelve algo raro.

Antes de empezar · 5 minutos, sin apuntes

  1. ¿Qué debe devolver /api/productos?max=abc?
  2. ¿Y /api/productos?categoria=inexistente?
  3. ¿Qué diferencia hay entre esas dos situaciones?

Listar con filtros

export async function listarProductos(peticion, respuesta, next) {
  try {
    const { categoria, max, orden, q } = peticion.query;
    const productos = await servicio.listar({ categoria, max, orden, busqueda: q });
    respuesta.json(productos);
  } catch (error) {
    next(error);
  }
}

La ruta no filtra: recoge los parámetros y se los pasa al servicio. Las funciones que hacen el trabajo son las de la UD3, que no saben nada de HTTP, y por eso las mismas sirven aquí y en el CLI.

Los parámetros llegan como texto

const maximo = max === undefined ? null : Number(max);
if (maximo !== null && (Number.isNaN(maximo) || maximo < 0)) {
  throw new ErrorDeValidacion([{ campo: "max", mensaje: "Debe ser un número no negativo" }]);
}

Un filtro inválido es un 400; un filtro sin resultados es un 200 vacío

Pedir max=abc es una petición mal formada: 400 con su detalle. Pedir una categoría que existe pero no tiene productos es una petición perfectamente válida cuya respuesta es una lista vacía, con 200.

Devolver 404 por una lista vacía es un error de diseño frecuente: la colección existe, y el cliente sabe leer un array de cero elementos.

Obtener uno

export async function obtenerProducto(peticion, respuesta, next) {
  try {
    const id = Number(peticion.params.id);
    if (!Number.isInteger(id) || id < 1) {
      throw new ErrorDeValidacion([{ campo: "id", mensaje: "Debe ser un entero positivo" }]);
    }

    const producto = await servicio.obtener(id);
    if (!producto) throw new ErrorNoEncontrado("Producto no encontrado");

    respuesta.json(producto);
  } catch (error) {
    next(error);
  }
}

Decidir qué se devuelve

function aRespuesta({ id, nombre, precio, categoria, stock }) {
  return { id, nombre, precio, categoria, disponible: stock > 0 };
}

Devolver el objeto en su forma almacenada resulta inmediato y expone información que no corresponde publicar: notas internas, márgenes o el propio stock. Una función que decide la forma pública del recurso deja explícito qué sale, y evita que añadir un campo interno lo publique sin querer.

Tarea 4 · Las lecturas completas

  1. Implementa la lista con filtro por categoría, texto y precio máximo.
  2. Añade orden por dos campos, ascendente y descendente.
  3. Implementa la obtención por identificador con sus dos errores.
  4. Valida todos los parámetros y responde 400 con detalles.
  5. Escribe la función que decide la forma pública del recurso.
  6. Prueba los ocho casos con tu fichero de peticiones.
Objetivo mínimoLas dos rutas con filtros validados y los códigos correctos.
Si lo tienesAñade paginación con limite y pagina, y devuelve el total.
RetoPermite elegir los campos devueltos con un parámetro, validando los nombres.

Checkpoint · fin de la sesión 4

  • La ruta recoge y valida; el servicio decide.
  • Un filtro inválido responde 400 con detalle.
  • Una búsqueda sin resultados responde 200 y una lista vacía.
  • Decides explícitamente qué campos salen.
Ver respuestas

1 · Un 400: el parámetro está mal formado.

2 · Un 200 con una lista vacía: la petición era válida.

3 · Para no publicar campos internos al añadirlos al modelo.