Saltearse al contenido

Filtros de Excepción

Los filtros de excepción te dan un único lugar para traducir cualquier error lanzado a una respuesta HTTP consistente — sin try/catch en cada handler.

Sin filtros, distintos plugins lanzan distintos tipos de error con distintas formas. Los handlers acaban con bloques try/catch dispersos que producen respuestas inconsistentes.

// Sin filtros — manejo disperso
@Get('/users/:id')
async getUser(@Param('id') id: string) {
try {
return await db.findUser(id);
} catch (err) {
if (err instanceof DrizzleError) return c.json({ error: '...' }, 500);
throw err;
}
}

Con filtros de excepción, defines la traducción una vez y todas las rutas se benefician.

Implementa la interfaz ExceptionFilter<T> y decora con @Catch:

import { ExceptionFilter, Catch } from 'veloce-ts';
import type { Context } from 'hono';
@Catch(DrizzleError)
class DatabaseExceptionFilter implements ExceptionFilter<DrizzleError> {
catch(error: DrizzleError, c: Context): Response {
return c.json(
{ error: 'Error de base de datos', code: error.code },
500
);
}
}

@Catch acepta una o más clases de error. El filtro se aplica cuando cualquier comprobación instanceof coincide.

const app = new Veloce();
app.useFilter(new DatabaseExceptionFilter());
app.useFilter(new ValidationExceptionFilter());
await app.compile();

Los filtros se comprueban en orden de registro. El primero que coincide gana — los siguientes se omiten.

@Catch(TypeError, RangeError)
class JavaScriptErrorFilter implements ExceptionFilter {
catch(error: Error, c: Context): Response {
return c.json({ error: error.message }, 500);
}
}
import { ExceptionFilter, Catch } from 'veloce-ts';
import { ZodError } from 'zod';
import type { Context } from 'hono';
@Catch(ZodError)
class ValidationExceptionFilter implements ExceptionFilter<ZodError> {
catch(error: ZodError, c: Context): Response {
return c.json(
{
error: 'Validación fallida',
details: error.issues.map(i => ({
field: i.path.join('.'),
message: i.message,
})),
},
422
);
}
}
class UserNotFoundError extends Error {
constructor(public readonly userId: string) {
super(`Usuario ${userId} no encontrado`);
}
}
@Catch(UserNotFoundError)
class UserNotFoundFilter implements ExceptionFilter<UserNotFoundError> {
catch(error: UserNotFoundError, c: Context): Response {
return c.json({ error: 'Usuario no encontrado', userId: error.userId }, 404);
}
}
// En tu servicio — solo lanza, el filtro se encarga del resto
async getUser(id: string) {
const user = await db.findUser(id);
if (!user) throw new UserNotFoundError(id);
return user;
}
Petición → Handler de ruta (lanza) → FilterManager
→ Filtro 1: comprobación instanceof → omitir si no coincide
→ Filtro 2: comprobación instanceof → COINCIDE → devolver Response
→ (sin coincidencia) → manejador de errores por defecto del framework

FilterManager está exportado para integraciones que necesitan activar filtros fuera del router (p. ej. manejo de errores de WebSocket):

import { FilterManager } from 'veloce-ts';
const manager = app.getFilterManager();
const response = manager.handle(error, c);
if (response) return response;
  • Un filtro por dominio de error — mantén los filtros pequeños y enfocados.
  • Registra en orden de especificidad — primero las clases de error más específicas; el filtro genérico Error al final.
  • Nunca silencies errores — siempre devuelve una Response o relanza.
// Filtros específicos primero
app.useFilter(new UserNotFoundFilter());
app.useFilter(new DatabaseExceptionFilter());
app.useFilter(new ValidationExceptionFilter());
// Genérico al final
app.useFilter(new GenericErrorFilter());