Cómo construir APIs escalables con Route Handlers de Next.js
Guía completa para diseñar APIs escalables y listas para producción con Route Handlers de Next.js, TypeScript, validación y manejo de errores.

Introducción
Construir APIs que escalen no es solo cuestión de manejar más peticiones: se trata de diseñar sistemas que sigan siendo mantenibles, testeables y fiables a medida que tu aplicación crece. En este post repaso los patrones que uso al construir APIs de producción con Route Handlers de Next.js.
¿Por qué Route Handlers de Next.js?
El App Router de Next.js introdujo los Route Handlers como una forma de primera clase para construir endpoints de API. Ofrecen varias ventajas sobre las API routes heredadas:
- Colocación — Las rutas de API viven junto a tus páginas en el directorio app
- APIs estándar web — Construidas sobre los objetos
RequestyResponse - Soporte de Edge Runtime — Despliega más cerca de tus usuarios
- Estático y dinámico — Elige la estrategia de renderizado adecuada por ruta
Configurando una estructura escalable
Así organizo las rutas de API en proyectos grandes:
// src/app/api/v1/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
tags: z.array(z.string()).max(10).optional(),
});
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const validated = createPostSchema.parse(body);
// Process the validated data
const post = await createPost(validated);
return NextResponse.json(post, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ errors: error.flatten().fieldErrors },
{ status: 400 },
);
}
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}Validación en los límites
Un principio que sigo a rajatabla: valida en los límites del sistema, confía internamente. Toda entrada externa —cuerpos de petición, parámetros de consulta, cabeceras— se valida con esquemas de Zod antes de tocar la lógica de negocio.
const querySchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
sort: z.enum(["date", "title", "popularity"]).default("date"),
});
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const query = querySchema.parse(Object.fromEntries(searchParams));
const posts = await getPosts(query);
return NextResponse.json(posts);
}Patrones de manejo de errores
Una estrategia de manejo de errores consistente hace que la depuración y la integración con clientes sean mucho más sencillas:
class AppError extends Error {
constructor(
message: string,
public statusCode: number,
public code: string,
) {
super(message);
}
}
function handleError(error: unknown): NextResponse {
if (error instanceof AppError) {
return NextResponse.json(
{ error: error.message, code: error.code },
{ status: error.statusCode },
);
}
console.error("Unhandled error:", error);
return NextResponse.json(
{ error: "Internal server error", code: "INTERNAL_ERROR" },
{ status: 500 },
);
}Limitación de peticiones (rate limiting)
Para APIs en producción, el rate limiting es imprescindible. Aquí tienes un enfoque sencillo en memoria adecuado para serverless:
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
function rateLimit(ip: string, maxRequests = 60, windowMs = 60000): boolean {
const now = Date.now();
const entry = rateLimitMap.get(ip);
if (!entry || now > entry.resetAt) {
rateLimitMap.set(ip, { count: 1, resetAt: now + windowMs });
return true;
}
if (entry.count >= maxRequests) return false;
entry.count++;
return true;
}Conclusiones clave
- Valida en los límites — Usa esquemas de Zod para toda entrada externa
- Respuestas de error consistentes — Estandariza el formato de errores desde el principio
- Aplica rate limiting a todo — Protege tus APIs contra abusos
- Tipa todo — TypeScript detecta errores antes de que lleguen a producción
- Mantén los handlers ligeros — Extrae la lógica de negocio en módulos separados
Construir APIs escalables es un proceso iterativo. Empieza con estos patrones, mide tu uso real y optimiza donde los datos te lo indiquen.


