Aprende a crear potentes endpoints de API usando los Route Handlers de Next.js. Esta guía cubre todo, desde la configuración básica hasta técnicas avanzadas, con ejemplos prácticos y mejores prácticas.
Route Handlers de Next.js: Una Guía Completa para la Creación de Endpoints de API
Next.js ha revolucionado la forma en que construimos aplicaciones web con sus potentes características como el renderizado del lado del servidor, la generación de sitios estáticos y, ahora, los Route Handlers. Los Route Handlers proporcionan una forma flexible y eficiente de crear endpoints de API directamente dentro de tu aplicación Next.js. Esta guía explora el concepto de los Route Handlers, sus beneficios y cómo usarlos eficazmente para construir APIs robustas.
¿Qué son los Route Handlers de Next.js?
Los Route Handlers son funciones definidas dentro del directorio app
de un proyecto Next.js que manejan las solicitudes HTTP entrantes. A diferencia del antiguo enfoque pages/api
(que utiliza Rutas de API), los Route Handlers ofrecen una manera más optimizada y flexible de definir endpoints de API junto a tus componentes de React. Son esencialmente funciones sin servidor ejecutadas en el borde o en el entorno de servidor que elijas.
Piensa en los Route Handlers como la lógica del backend de tu aplicación Next.js, responsables de procesar solicitudes, interactuar con bases de datos y devolver respuestas.
Beneficios de Usar Route Handlers
- Colocación: Los Route Handlers residen directamente junto a tus componentes de React dentro del directorio
app
, promoviendo una mejor organización y mantenibilidad del código. - Soporte para TypeScript: El soporte integrado para TypeScript garantiza la seguridad de tipos y una mejor experiencia para el desarrollador.
- Integración de Middleware: Integra fácilmente middleware para tareas como autenticación, autorización y validación de solicitudes.
- Soporte para Streaming: Los Route Handlers pueden transmitir datos, permitiéndote enviar respuestas de forma incremental, lo cual es beneficioso para grandes conjuntos de datos o procesos de larga duración.
- Edge Functions: Despliega los Route Handlers como Edge Functions para obtener respuestas de baja latencia más cerca de tus usuarios, aprovechando las CDNs globales.
- Diseño de API Simplificado: Los Route Handlers proporcionan una API limpia e intuitiva para manejar solicitudes y respuestas.
- Integración con Server Actions: La estrecha integración con las Server Actions permite una comunicación fluida entre tus componentes del lado del cliente y la lógica del lado del servidor.
Configurando tu Proyecto de Next.js
Antes de sumergirte en los Route Handlers, asegúrate de tener un proyecto de Next.js configurado con el directorio app
. Si estás comenzando un nuevo proyecto, usa el siguiente comando:
npx create-next-app@latest my-nextjs-app
Elige el directorio app
durante el proceso de configuración para habilitar el nuevo sistema de enrutamiento.
Creando tu Primer Route Handler
Vamos a crear un endpoint de API simple que devuelva una respuesta JSON. Crea un nuevo directorio dentro del directorio app
, por ejemplo, /app/api/hello
. Dentro de este directorio, crea un archivo llamado route.ts
(o route.js
si no estás usando TypeScript).
Aquí está el código para tu primer Route Handler:
// app/api/hello/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
}
Explicación:
import { NextResponse } from 'next/server';
: Importa el objetoNextResponse
, que se utiliza para construir respuestas de API.export async function GET(request: Request) { ... }
: Define una función asíncrona que maneja las solicitudes GET al endpoint/api/hello
. El parámetrorequest
proporciona acceso al objeto de la solicitud entrante.return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
: Crea una respuesta JSON con un mensaje y la devuelve usandoNextResponse.json()
.
Ahora, puedes acceder a este endpoint navegando a /api/hello
en tu navegador o usando una herramienta como curl
o Postman
.
Manejando Diferentes Métodos HTTP
Los Route Handlers soportan varios métodos HTTP como GET, POST, PUT, DELETE, PATCH y OPTIONS. Puedes definir funciones separadas para cada método dentro del mismo archivo route.ts
.
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Lógica para recuperar todos los usuarios de la base de datos
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // Datos de ejemplo
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json(); // Analiza el cuerpo de la solicitud como JSON
// Lógica para crear un nuevo usuario en la base de datos usando 'data'
const newUser = { id: 3, name: data.name, email: data.email }; // Ejemplo
return NextResponse.json(newUser, { status: 201 }); // Devuelve el nuevo usuario con un código de estado 201 Created
}
Explicación:
- La función
GET
recupera una lista de usuarios (simulada aquí) y la devuelve como una respuesta JSON. - La función
POST
analiza el cuerpo de la solicitud como JSON, crea un nuevo usuario (simulado) y devuelve el nuevo usuario con un código de estado 201 Created.
Accediendo a los Datos de la Solicitud
El objeto request
proporciona acceso a diversa información sobre la solicitud entrante, incluyendo encabezados, parámetros de consulta y el cuerpo de la solicitud.
Encabezados
Puedes acceder a los encabezados de la solicitud usando la propiedad request.headers
:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent');
console.log('User Agent:', userAgent);
return NextResponse.json({ userAgent });
}
Parámetros de Consulta
Para acceder a los parámetros de consulta, puedes usar el constructor URL
:
export async function GET(request: Request) {
const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const id = searchParams.get('id');
console.log('ID:', id);
return NextResponse.json({ id });
}
Cuerpo de la Solicitud
Para solicitudes POST, PUT y PATCH, puedes acceder al cuerpo de la solicitud usando los métodos request.json()
o request.text()
, dependiendo del tipo de contenido.
export async function POST(request: Request) {
const data = await request.json();
console.log('Data:', data);
return NextResponse.json({ receivedData: data });
}
Devolviendo Respuestas
El objeto NextResponse
se utiliza para construir respuestas de API. Proporciona varios métodos para establecer encabezados, códigos de estado y cuerpos de respuesta.
Respuestas JSON
Usa el método NextResponse.json()
para devolver respuestas JSON:
return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });
Respuestas de Texto
Usa el constructor new Response()
para devolver respuestas de texto plano:
return new Response('Hello, world!', { status: 200, headers: { 'Content-Type': 'text/plain' } });
Redirecciones
Usa NextResponse.redirect()
para redirigir a los usuarios a una URL diferente:
import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.redirect(new URL('/new-location', request.url));
}
Estableciendo Encabezados
Puedes establecer encabezados personalizados usando la opción headers
en NextResponse.json()
o new Response()
:
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });
Integración de Middleware
El middleware te permite ejecutar código antes de que una solicitud sea manejada por tu Route Handler. Esto es útil para la autenticación, autorización, registro y otras preocupaciones transversales.
Para crear un middleware, crea un archivo llamado middleware.ts
(o middleware.js
) en el directorio app
o en cualquier subdirectorio. El middleware se aplicará a todas las rutas dentro de ese directorio y sus subdirectorios.
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'], // Aplica este middleware a rutas que comiencen con /protected/
};
Explicación:
- La función
middleware
verifica la existencia de un token de autenticación en las cookies de la solicitud. - Si falta el token, redirige al usuario a la página de inicio de sesión.
- De lo contrario, permite que la solicitud continúe hacia el Route Handler.
- El objeto
config
especifica que este middleware solo debe aplicarse a las rutas que comiencen con/protected/
.
Manejo de Errores
El manejo adecuado de errores es crucial para construir APIs robustas. Puedes usar bloques try...catch
para manejar excepciones y devolver respuestas de error apropiadas.
export async function GET(request: Request) {
try {
// Simula un error
throw new Error('Something went wrong!');
} catch (error: any) {
console.error('Error:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Explicación:
- El bloque
try...catch
captura cualquier excepción que ocurra dentro del Route Handler. - En el bloque
catch
, el error se registra en la consola y se devuelve una respuesta de error con un código de estado 500 Internal Server Error.
Respuestas en Streaming
Los Route Handlers soportan respuestas en streaming, lo que te permite enviar datos de forma incremental al cliente. Esto es particularmente útil para grandes conjuntos de datos o procesos de larga duración.
import { Readable } from 'stream';
import { NextResponse } from 'next/server';
async function* generateData() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simula un retraso
yield `Data chunk ${i}\n`;
}
}
export async function GET(request: Request) {
const readableStream = Readable.from(generateData());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
Explicación:
- La función
generateData
es un generador asíncrono que produce fragmentos de datos con un retraso. - El método
Readable.from()
crea un stream legible a partir del generador. - El objeto
Response
se crea con el stream legible como cuerpo, y el encabezadoContent-Type
se establece entext/plain
.
Autenticación y Autorización
Asegurar tus endpoints de API es crucial. Puedes implementar la autenticación y la autorización usando middleware o directamente dentro de tus Route Handlers.
Autenticación
La autenticación verifica la identidad del usuario que realiza la solicitud. Los métodos de autenticación comunes incluyen:
- JWT (JSON Web Tokens): Genera un token tras un inicio de sesión exitoso y verifícalo en solicitudes posteriores.
- Autenticación basada en sesión: Usa cookies para almacenar identificadores de sesión y verificarlos en cada solicitud.
- OAuth: Delega la autenticación a un proveedor de terceros como Google o Facebook.
Aquí hay un ejemplo de autenticación JWT usando middleware:
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';
const secret = process.env.JWT_SECRET || 'your-secret-key'; // Reemplaza con un secreto fuerte y generado aleatoriamente
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.json({ message: 'Authentication required' }, { status: 401 });
}
try {
jwt.verify(token, secret);
return NextResponse.next();
} catch (error) {
return NextResponse.json({ message: 'Invalid token' }, { status: 401 });
}
}
export const config = {
matcher: ['/api/protected/:path*'],
};
Autorización
La autorización determina a qué recursos puede acceder un usuario. Esto generalmente se basa en roles o permisos.
Puedes implementar la autorización dentro de tus Route Handlers verificando los roles o permisos del usuario y devolviendo un error si no tienen acceso.
// app/api/admin/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Asume que tienes una función para obtener el rol del usuario desde el token o la sesión
const userRole = await getUserRole(request);
if (userRole !== 'admin') {
return NextResponse.json({ message: 'Unauthorized' }, { status: 403 });
}
// Lógica para recuperar datos de administrador
const adminData = { message: 'Admin data' };
return NextResponse.json(adminData);
}
async function getUserRole(request: Request): Promise {
// Reemplaza con tu lógica real para extraer el rol del usuario de la solicitud
// Esto podría implicar verificar un token JWT o una sesión
return 'admin'; // Ejemplo: rol hardcodeado para la demostración
}
Desplegando Route Handlers
Los Route Handlers se despliegan como funciones sin servidor en tu proveedor de hosting elegido. Next.js soporta varias plataformas de despliegue, incluyendo Vercel, Netlify, AWS y más.
Para Vercel, el despliegue es tan simple como conectar tu repositorio de Git a Vercel y subir tu código. Vercel detecta automáticamente tu proyecto de Next.js y despliega tus Route Handlers como funciones sin servidor.
Técnicas Avanzadas
Edge Functions
Los Route Handlers se pueden desplegar como Edge Functions, que se ejecutan en el borde de una CDN, más cerca de tus usuarios. Esto puede reducir significativamente la latencia y mejorar el rendimiento.
Para desplegar un Route Handler como una Edge Function, añade el runtime edge
a tu archivo route.ts
:
export const runtime = 'edge';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Hello from the Edge!' });
}
Server Actions
Las Server Actions te permiten ejecutar código del lado del servidor directamente desde tus componentes de React. Los Route Handlers y las Server Actions funcionan juntos sin problemas, permitiéndote construir aplicaciones complejas con facilidad.
Aquí hay un ejemplo de cómo usar una Server Action para llamar a un Route Handler:
// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
async function handleSubmit(data: FormData) {
'use server';
const name = data.get('name');
const email = data.get('email');
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name, email }),
});
if (response.ok) {
router.refresh(); // Refresca la página para reflejar los cambios
}
}
export default function MyComponent() {
const router = useRouter();
return (
);
}
Almacenamiento en Caché
El almacenamiento en caché puede mejorar significativamente el rendimiento de tus endpoints de API. Puedes usar el encabezado Cache-Control
para controlar cómo tus respuestas son almacenadas en caché por los navegadores y las CDNs.
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });
Este ejemplo establece el encabezado Cache-Control
en public, max-age=3600
, lo que indica a los navegadores y CDNs que almacenen en caché la respuesta durante una hora.
Mejores Prácticas
- Usa TypeScript: Aprovecha la seguridad de tipos de TypeScript para mejorar la calidad del código y prevenir errores.
- Valida las Solicitudes: Valida las solicitudes entrantes para garantizar la integridad de los datos y prevenir entradas maliciosas.
- Maneja Errores con Gracia: Implementa un manejo de errores adecuado para proporcionar mensajes de error informativos a los clientes.
- Asegura tus Endpoints: Implementa autenticación y autorización para proteger tus endpoints de API.
- Usa Middleware: Usa middleware para preocupaciones transversales como autenticación, registro y validación de solicitudes.
- Almacena Respuestas en Caché: Usa el almacenamiento en caché para mejorar el rendimiento de tus endpoints de API.
- Monitorea tus APIs: Monitorea tus APIs para identificar y resolver problemas rápidamente.
- Documenta tus APIs: Documenta tus APIs para que sean fáciles de usar para otros desarrolladores. Considera usar herramientas como Swagger/OpenAPI para la documentación de la API.
Ejemplos del Mundo Real
Aquí hay algunos ejemplos del mundo real de cómo se pueden usar los Route Handlers:
- API de comercio electrónico: Crea endpoints de API para gestionar productos, pedidos y usuarios.
- API de redes sociales: Crea endpoints de API para publicar tweets, seguir a usuarios y recuperar líneas de tiempo.
- API de Sistema de Gestión de Contenidos (CMS): Crea endpoints de API para gestionar contenido, usuarios y configuraciones.
- API de análisis de datos: Crea endpoints de API para recopilar y analizar datos. Por ejemplo, un Route Handler podría recibir datos de píxeles de seguimiento en diferentes sitios web y agregar la información para la elaboración de informes.
Ejemplo de comercio electrónico internacional: Un Route Handler utilizado para recuperar los precios de los productos según el país del usuario. El endpoint podría usar la geolocalización de la solicitud (derivada de la dirección IP) para determinar la ubicación del usuario y devolver los precios en la moneda apropiada. Esto contribuye a una experiencia de compra localizada.
Ejemplo de autenticación global: Un Route Handler que implementa la autenticación multifactor (MFA) para usuarios de todo el mundo. Esto podría implicar el envío de códigos SMS o el uso de aplicaciones de autenticación, respetando al mismo tiempo las regulaciones de privacidad y las infraestructuras de telecomunicaciones de las diferentes regiones.
Entrega de contenido multilingüe: Un Route Handler que entrega contenido en el idioma preferido del usuario. Esto se puede determinar a partir del encabezado `Accept-Language` en la solicitud. Este ejemplo resalta la necesidad de una codificación UTF-8 adecuada y soporte para idiomas de derecha a izquierda cuando sea apropiado.
Conclusión
Los Route Handlers de Next.js proporcionan una forma potente y flexible de crear endpoints de API directamente dentro de tu aplicación Next.js. Al aprovechar los Route Handlers, puedes construir APIs robustas con facilidad, colocar tu lógica de backend junto a tus componentes de React y aprovechar características como middleware, streaming y Edge Functions.
Esta guía completa ha cubierto todo, desde la configuración básica hasta las técnicas avanzadas. Siguiendo las mejores prácticas descritas en esta guía, puedes construir APIs de alta calidad que sean seguras, de alto rendimiento y fáciles de mantener.