Skip to main content
Los endpoints en src/app/api/**/route.ts siguen un patrón consistente, en este orden:

1. requireAdmin() en todo /api/admin/*

Todo endpoint bajo /api/admin/* empieza llamando a requireAdmin() de @/lib/require-admin:
requireAdmin() devuelve { error, session }. Si error no es null, el handler lo retorna tal cual — ya es un 401 (sin sesión) o un 403 (sesión sin rol ADMIN).

2. Validación con Zod safeParse

Los searchParams o el body se validan con safeParse. Si falla, la respuesta es 400 con esta forma:
issues es la salida de parsed.error.flatten(). Si el endpoint recibe un body JSON, request.json() va en su propio try/catch, antes del try general. Un body mal formado es un error del cliente: responde 400 con { "error": "JSON invalido" } en vez de caer en el 500 del paso 4:

3. Listados paginados por cursor

Los listados paginan con take: limit + 1 y, si hay cursor, con cursor: { id: cursor } más skip: 1. La fila extra sirve para saber si hay otra página sin hacer un segundo count. La respuesta tiene siempre nextCursor y hasMore; el nombre de la clave con el array cambia según el recurso (users, camellos…):
El cursor es el id de la última fila de la página anterior — también en el listado de usuarios, donde antes era un cursor compuesto codificado en base64url. Ese cursor especial se retiró cuando el listado de usuarios pasó a poder ordenarse por columna: con orderBy variable, un id simple es suficiente y funciona igual que en camellos.

4. try/catch envolviendo todo

Todo el handler queda envuelto en try/catch. El catch registra el error con un prefijo que identifica el endpoint y responde 500:
Un catch sin console.error deja los 500 de producción sin rastro en los logs; no lo omitas aunque el handler sea corto. Cuando el handler comprueba unicidad con un findUnique previo (por ejemplo, el email en POST /api/register, POST /api/admin/users y PATCH /api/admin/users/:id), dos peticiones simultáneas pueden pasar la comprobación a la vez y la segunda choca con el índice único de la BD. El catch traduce ese P2002 de Prisma al mismo 409 que da la comprobación, antes de caer al 500:
El mismo razonamiento vale para las demás comprobaciones previas. Los endpoints admin de categorías y servicios (/api/admin/categories/:id, /api/admin/services/:id) traducen así los códigos de Prisma: Ojo con Restrict: Job → Service lo usa, así que un servicio con trabajos (incluso terminados) no se puede borrar. DELETE /api/admin/services/:id cuenta camellos y jobs antes de borrar y responde 409 con el motivo; el P2003 del catch solo cubre la carrera. Si el create escribe más de un campo único, hay que distinguir cuál falló. Con el driver adapter de MariaDB, el índice no llega en meta.target: ver violatesUniqueConstraint en src/app/api/register/camello/route.ts.

Idioma de los mensajes

Los mensajes de error de la API van en español sin tildes — "Parametros invalidos", "No autenticado", "Sin permisos de administrador" — porque son valores literales que el cliente puede llegar a comparar. La UI que los muestra sí usa tildes.