El stack moderno.
El ecosistema de Node.js para APIs evolucionó mucho. Express sigue siendo relevante pero su minimalismo extremo significa que tenés que tomar muchas decisiones por tu cuenta. En 2026, el stack que ofrece el mejor balance entre productividad y control es: Hono (o Fastify) para el servidor, Zod para validación, Prisma para la base de datos, y JWT para autenticación.
Estructura del proyecto.
La estructura que escala bien para APIs medianas:
src/
routes/ # Definición de endpoints por recurso
handlers/ # Lógica de cada endpoint
services/ # Lógica de negocio (sin HTTP)
db/ # Queries y modelos de Prisma
middleware/ # Auth, logging, rate limiting
schemas/ # Schemas de Zod para validación
lib/ # Utilidades compartidas
La separación entre handlers y services es clave. Los handlers hablan HTTP (request, response). Los services hablan negocio (datos, reglas). Un service debe poder ser llamado desde un handler HTTP, un cron job o un test sin diferencia.
Validación con Zod.
Nunca confíes en los datos que llegan de un request. Zod te permite definir el schema esperado y validar automáticamente:
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
role: z.enum(['admin', 'user']).default('user'),
});
type CreateUserInput = z.infer<typeof CreateUserSchema>;
// En el handler:
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
// result.data está tipado correctamente
Autenticación JWT.
La autenticación con JWT implica dos operaciones: generar el token al hacer login, y verificarlo en cada request protegido.
import jwt from 'jsonwebtoken';
// Generar token
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
);
// Middleware de verificación
export function requireAuth(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No autorizado' });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET!);
next();
} catch {
res.status(401).json({ error: 'Token inválido' });
}
}
Manejo de errores.
Creá una clase de error custom y un middleware global de manejo de errores. Esto centraliza el logging y garantiza respuestas consistentes:
class AppError extends Error {
constructor(
public message: string,
public statusCode: number = 500,
public code?: string
) { super(message); }
}
// Middleware global (último middleware antes de iniciar el servidor)
app.use((err, req, res, next) => {
const status = err instanceof AppError ? err.statusCode : 500;
const message = err instanceof AppError ? err.message : 'Error interno';
console.error(err);
res.status(status).json({ error: message, code: err.code });
});
Despliegue.
Para APIs Node.js en 2026, las mejores opciones son Railway (el más simple para proyectos chicos), Fly.io (mejor precio/performance para apps medianas) y AWS Lambda/Vercel Functions si la API tiene tráfico irregular. Evitá EC2 clásico a menos que tengas requerimientos muy específicos que justifiquen la complejidad operacional.