Tratamiento de Errores
Un sistema robusto de manejo de errores es esencial para APIs en producción.
Clase de Error Personalizada
typescript
// src/errors/AppError.tsexport class AppError extends Error { public readonly statusCode: number; public readonly isOperational: boolean; constructor(message: string, statusCode: number = 400) { super(message); this.statusCode = statusCode; this.isOperational = true; Error.captureStackTrace(this, this.constructor); }}Middleware Global de Errores
typescript
// src/middlewares/error.middleware.tsimport { Request, Response, NextFunction } from 'express';import { AppError } from '../errors/AppError';import { ZodError } from 'zod'; export const errorHandler = ( err: Error, req: Request, res: Response, next: NextFunction) => { // Errores de validación Zod if (err instanceof ZodError) { return res.status(400).json({ status: 'error', message: 'Error de validación', errors: err.errors, }); } // Nuestros errores personalizados if (err instanceof AppError) { return res.status(err.statusCode).json({ status: 'error', message: err.message, }); } // Log de errores inesperados console.error('Error inesperado:', err); // No exponer detalles en producción return res.status(500).json({ status: 'error', message: process.env.NODE_ENV === 'production' ? 'Error interno del servidor' : err.message, });};Uso en Services
typescript
// src/services/product.service.tsimport { AppError } from '../errors/AppError'; export class ProductService { async findById(id: string) { const product = await prisma.product.findUnique({ where: { id } }); if (!product) { throw new AppError('Producto no encontrado', 404); } return product; } async create(data: CreateProductDTO) { const exists = await prisma.product.findFirst({ where: { name: data.name }, }); if (exists) { throw new AppError('Ya existe un producto con este nombre', 409); } return prisma.product.create({ data }); }}Async Handler
typescript
// src/utils/async-handler.tsimport { Request, Response, NextFunction } from 'express'; export const asyncHandler = (fn: Function) => { return (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); };}; // Usorouter.get('/:id', asyncHandler(async (req, res) => { const product = await productService.findById(req.params.id); res.json(product);}));Handler 404
typescript
// Después de todas las rutasapp.use((req, res) => { res.status(404).json({ status: 'error', message: `Ruta ${req.method} ${req.path} no encontrada`, });}); // Error handler siempre al finalapp.use(errorHandler);Resumen
- ✅ Clase AppError personalizada
- ✅ Middleware de errores centralizado
- ✅ Manejo de errores async
- ✅ Logging y respuestas según ambiente
Próxima clase: Introducción a Bases de Datos! 🚀