API routes en Next.js (Route Handlers): GET, POST, autenticación | We.Inc
Ejemplos de Route Handlers de Next.js para copiar en el App Router: GET con query params, POST con validación, parámetros dinámicos como promesa, comprobaciones de autenticación, CORS y webhooks.
En el App Router, un endpoint de API es un archivo route.ts dentro de app, por ejemplo app/api/todos/route.ts. Exporta funciones async con el nombre de los métodos HTTP, como GET y POST; cada una recibe un Request y devuelve un Response, normalmente Response.json(). Los segmentos dinámicos llegan como una promesa params a la que haces await. Desde Next.js 15, los handlers GET son dinámicos por defecto.
Lo básico de los Route Handlers
Tarea
Código
Devolver JSON
return Response.json(data, { status: 200 })
Leer un cuerpo JSON
const body = await request.json()
Leer datos de formulario
const form = await request.formData()
Leer ?q=
request.nextUrl.searchParams.get("q")
Segmento dinámico
const { id } = await params
Cookies / cabeceras
await cookies(), await headers() desde next/headers
Las API routes del Pages Router usaban los objetos req y res. Los Route Handlers usan en su lugar los Request y Response estándar de la Web, lo que confunde a quien copia respuestas antiguas. Abajo tienes los patrones que necesitarás en una app real, todos escritos para el App Router actual, y un pequeño probador que muestra los códigos de estado que debería devolver un POST con validación.
Rellena el cuerpo de la petición simulada en el probador y elige si quien hace la petición ha iniciado sesión.
El probador aplica en tu navegador las mismas reglas de validación que el ejemplo POST de esta página.
Lee el código de estado y el JSON que devuelve, y luego cambia un campo para ver un 400, un 401 o un 201.
Copia el handler de ejemplo y sustituye la lista en memoria por tu base de datos.
Trata cada handler como un endpoint público. Comprueba la sesión dentro del handler aunque el proxy ya haya redirigido a los usuarios sin sesión.
Valida el cuerpo con una librería de esquemas como Zod y envía un 400 con los errores de cada campo, en lugar de dejar que un payload incorrecto lance un 500.
Un archivo route.ts no puede estar en la misma carpeta que un page.tsx; pon las APIs bajo app/api para mantenerlas separadas.
Si no exportas OPTIONS, Next.js lo responde por ti con una cabecera Allow que enumera los métodos que sí exportaste.
En los webhooks, lee el cuerpo en bruto con request.text() antes de parsearlo, porque la verificación de la firma necesita los bytes exactos.
Preguntas frecuentes
¿Están obsoletas las API routes de Next.js?
Las rutas de pages/api siguen funcionando en el Pages Router. En el App Router, el equivalente es un Route Handler: un archivo route.ts que usa los objetos Request y Response de la Web y no necesita configuración de bodyParser.
¿Qué métodos HTTP puede exportar un route.ts?
GET, POST, PUT, PATCH, DELETE, HEAD y OPTIONS. Llamar a un método que no exportaste devuelve 405 Method Not Allowed.
¿Por qué tengo que hacer await de params?
Desde Next.js 15, el params del argumento de contexto es una promesa. Escribe const { id } = await params. El tipo auxiliar RouteContext, generado por next dev, next build o next typegen, te da params tipados a partir de la ruta.
¿Se cachean los Route Handlers GET?
Ya no por defecto. Next.js 15 cambió los handlers GET de estáticos a dinámicos. Vuelve a activar la caché con export const dynamic = 'force-static' o con un valor de revalidate. Con Cache Components activado, pon use cache en una función auxiliar que invoque el handler, no en el cuerpo del handler.
¿Uso un Route Handler o una Server Action?
Usa una Server Action para las mutaciones que disparan tus propios formularios y botones. Usa un Route Handler cuando algo fuera de tu árbol de React invoca la URL: una app móvil, un webhook, un cron job o un script de terceros.
Estructura de archivos
La ruta de la carpeta es la URL. app/api/todos/route.ts responde a /api/todos, y app/api/todos/[id]/route.ts responde a /api/todos/42. Una carpeta comodín como [...slug] pasa un array de segmentos.
Los Route Handlers comparten la configuración de segmento con las páginas, así que los mismos exports dynamic, revalidate y runtime se aplican a ellos.
Códigos de estado que los clientes entienden
Envía 201 después de crear algo, 400 cuando la entrada es incorrecta, 401 cuando nadie ha iniciado sesión, 403 cuando el usuario con sesión no tiene permiso, 404 cuando el registro no existe y 405 para métodos no admitidos. Así el frontend puede decidir según el código en lugar de analizar el texto del error.
Devuelve una forma JSON coherente para los errores, como { error: string, fields?: object }, para que un solo helper de fetch pueda gestionarlos todos.
Lista de comprobación de seguridad
Verifica la sesión, comprueba que el usuario es dueño del registro que está cambiando, valida el payload y nunca devuelvas secretos ni filas completas de usuario. La guía oficial de autenticación muestra este patrón en dos pasos: un 401 si no hay sesión y un 403 si el rol no es el correcto.
Pon cabeceras CORS solo en los handlers que de verdad necesitan llamar otros orígenes, y enumera los orígenes concretos en lugar de un asterisco siempre que haya cookies de por medio.
Ejemplos de Route Handlers
GET con una consulta de búsqueda
app/api/todos/route.ts. El parámetro q filtra la lista; una consulta vacía lo devuelve todo.
POST con validación Zod y comprobación de autenticación
Devuelve 401 sin sesión, 400 con errores por campo si el cuerpo es incorrecto y, en otro caso, 201 con el nuevo registro. getSession representa tu librería de autenticación.
Segmento dinámico con await de params
app/api/todos/[id]/route.ts. params es una promesa en Next.js 15 y posteriores.
CORS para un único origen de confianza
Responde a la petición preflight del navegador y a la petición real con el mismo origen permitido.
Llamar al handler desde un Client Component
El helper de fetch lee primero el código de estado, para que el formulario pueda mostrar los errores por campo de un 400.
API routes del Pages Router frente a Route Handlers
La misma tarea, APIs distintas
pages/api/x.ts
app/api/x/route.ts
Handler
export default function handler(req, res)
export async function GET(request)
Enviar JSON
res.status(200).json(data)
return Response.json(data)
Distinguir el método
if (req.method === "POST")
una función POST exportada aparte
Parseo del cuerpo
req.body (configuración de bodyParser)
await request.json() o request.formData()
Query string
request.nextUrl.searchParams
Probador de POST
Edita un cuerpo JSON, elige si quien hace la petición ha iniciado sesión y mira el código de estado y la respuesta que devolvería el ejemplo POST con validación.
Método
Cuerpo de la petición (JSON)
Quien hace la petición tiene una sesión válida
Respuesta
PUT muestra el 405 que envía Next.js para un método que el archivo no exporta.