Volver a Next.js Básico
Route Handlers (API)
Los Route Handlers permiten construir APIs REST dentro de tu app Next.js usando archivos route.ts.
Crear un Route Handler
// app/api/productos/route.ts
import { NextRequest, NextResponse } from 'next/server';
// GET /api/productos
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const categoria = searchParams.get('categoria');
const productos = await db.producto.findMany({
where: categoria ? { categoria } : {},
});
return NextResponse.json(productos);
}
// POST /api/productos
export async function POST(request: NextRequest) {
const body = await request.json();
// Validar
if (!body.nombre || !body.precio) {
return NextResponse.json(
{ error: 'nombre y precio son requeridos' },
{ status: 400 }
);
}
const producto = await db.producto.create({ data: body });
return NextResponse.json(producto, { status: 201 });
}
Route Handlers Dinámicos
// app/api/productos/[id]/route.ts
interface Params {
params: Promise<{ id: string }>;
}
// GET /api/productos/:id
export async function GET(request: NextRequest, { params }: Params) {
const { id } = await params;
const producto = await db.producto.findUnique({ where: { id } });
if (!producto) {
return NextResponse.json({ error: 'No encontrado' }, { status: 404 });
}
return NextResponse.json(producto);
}
// PUT /api/productos/:id
export async function PUT(request: NextRequest, { params }: Params) {
const { id } = await params;
const body = await request.json();
const actualizado = await db.producto.update({ where: { id }, data: body });
return NextResponse.json(actualizado);
}
// DELETE /api/productos/:id
export async function DELETE(request: NextRequest, { params }: Params) {
const { id } = await params;
await db.producto.delete({ where: { id } });
return new NextResponse(null, { status: 204 });
}
Helpers de Respuesta
import { NextResponse } from 'next/server';
// Respuesta JSON
NextResponse.json({ dato: 'valor' }, { status: 200 });
// Redirección
NextResponse.redirect(new URL('/login', request.url));
// Establecer cookies (seguras)
const response = NextResponse.json({ ok: true });
response.cookies.set('token', 'abc123', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7, // 1 semana
});
return response;
Middleware
// middleware.ts (en la raíz del proyecto)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value;
// Proteger rutas del dashboard
if (request.nextUrl.pathname.startsWith('/dashboard')) {
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/admin/:path*'],
};
Llamar la API desde Componentes de Cliente
'use client';
import { useState } from 'react';
export function FormularioCrearProducto() {
const [nombre, setNombre] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const res = await fetch('/api/productos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nombre, precio: 9.99 }),
});
if (res.ok) {
alert('¡Producto creado!');
setNombre('');
}
};
return (
<form onSubmit={handleSubmit}>
<input value={nombre} onChange={e => setNombre(e.target.value)} />
<button type="submit">Crear</button>
</form>
);
}
Server Actions vs Route Handlers: Prefiere Server Actions para mutaciones de formularios en tu propia UI. Usa Route Handlers al construir una API pública consumida por apps móviles o clientes externos.