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.