Guía de Decoradores
Guía de Decoradores
Sección titulada «Guía de Decoradores»Guía completa para usar decoradores en Veloce.
Tabla de Contenidos
Sección titulada «Tabla de Contenidos»- Introducción
- Decoradores de Clase
- Decoradores de Método
- Decoradores de Parámetro
- Combinando Decoradores
- Mejores Prácticas
Introducción
Sección titulada «Introducción»Veloce usa decoradores de TypeScript para proporcionar una API limpia y declarativa para definir rutas, validación y dependencias. Los decoradores son funciones especiales que pueden modificar clases, métodos y parámetros en tiempo de diseño.
Configuración de TypeScript
Sección titulada «Configuración de TypeScript»Para usar decoradores, habilítalos en tu tsconfig.json:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true }}Decoradores de Clase
Sección titulada «Decoradores de Clase»@Controller(prefix?: string)
Sección titulada «@Controller(prefix?: string)»Marca una clase como controlador y opcionalmente establece un prefijo de ruta para todas las rutas.
@Controller('/api/users')class UserController { @Get('/') async list() { // Ruta: GET /api/users/ return []; }
@Get('/:id') async get(@Param('id') id: string) { // Ruta: GET /api/users/:id return { id }; }}Sin prefijo:
@Controller()class RootController { @Get('/health') async health() { // Ruta: GET /health return { status: 'ok' }; }}@WebSocket(path: string)
Sección titulada «@WebSocket(path: string)»Marca una clase como manejador de WebSocket.
@WebSocket('/chat')class ChatHandler { @OnConnect() handleConnect(client: WebSocketConnection) { console.log('Cliente conectado'); }
@OnMessage() handleMessage(client: WebSocketConnection, message: any) { client.broadcast(message); }}@Resolver()
Sección titulada «@Resolver()»Marca una clase como resolver de GraphQL.
@Resolver()class UserResolver { @Query() async users() { return []; }
@Mutation() async createUser(@Arg('input', UserInput) input: any) { return { id: 1, ...input }; }}Decoradores de Método
Sección titulada «Decoradores de Método»Decoradores de Métodos HTTP
Sección titulada «Decoradores de Métodos HTTP»@Get(path?: string)
Sección titulada «@Get(path?: string)»@Controller('/users')class UserController { @Get() async list() { // GET /users return []; }
@Get('/:id') async get(@Param('id') id: string) { // GET /users/:id return { id }; }
@Get('/search') async search(@Query() query: any) { // GET /users/search?q=... return []; }}@Post(path?: string)
Sección titulada «@Post(path?: string)»@Post()async create(@Body(UserSchema) user: User) { // POST /users return user;}
@Post('/bulk')async createMany(@Body(z.array(UserSchema)) users: User[]) { // POST /users/bulk return users;}@Put(path?: string)
Sección titulada «@Put(path?: string)»@Put('/:id')async update( @Param('id') id: string, @Body(UserSchema) user: User) { // PUT /users/:id return { id, ...user };}@Delete(path?: string)
Sección titulada «@Delete(path?: string)»@Delete('/:id')async delete(@Param('id') id: string) { // DELETE /users/:id return { success: true };}@Patch(path?: string)
Sección titulada «@Patch(path?: string)»@Patch('/:id')async patch( @Param('id') id: string, @Body(PartialUserSchema) data: Partial<User>) { // PATCH /users/:id return { id, ...data };}@All(path?: string)
Sección titulada «@All(path?: string)»Responde a todos los métodos HTTP.
@All('/webhook')async webhook() { // Maneja GET, POST, PUT, DELETE, etc. return { received: true };}Decoradores de Parámetro
Sección titulada «Decoradores de Parámetro»@Body(schema?: ZodSchema)
Sección titulada «@Body(schema?: ZodSchema)»Extrae y valida el cuerpo de la petición.
const UserSchema = z.object({ name: z.string().min(2), email: z.string().email(), age: z.number().min(18).optional(),});
@Post('/users')async createUser(@Body(UserSchema) user: z.infer<typeof UserSchema>) { // user está validado y tipado return user;}@Query(schema?: ZodSchema)
Sección titulada «@Query(schema?: ZodSchema)»Extrae y valida parámetros de consulta.
const SearchSchema = z.object({ q: z.string().min(1), page: z.string().transform(Number).default('1'), limit: z.string().transform(Number).default('10'),});
@Get('/search')async search(@Query(SearchSchema) query: z.infer<typeof SearchSchema>) { return { query: query.q, page: query.page, limit: query.limit, };}@Param(name?: string)
Sección titulada «@Param(name?: string)»Extrae parámetros de ruta.
@Get('/users/:id')async getUser(@Param('id') id: string) { return { id };}
@Get('/users/:userId/posts/:postId')async getPost( @Param('userId') userId: string, @Param('postId') postId: string) { return { userId, postId };}@Header(name?: string, schema?: ZodSchema)
Sección titulada «@Header(name?: string, schema?: ZodSchema)»Extrae y valida encabezados.
@Get('/protected')async getProtected(@Header('authorization') auth: string) { return { auth };}@Cookie(name?: string, schema?: ZodSchema)
Sección titulada «@Cookie(name?: string, schema?: ZodSchema)»Extrae y valida cookies.
@Get('/profile')async getProfile(@Cookie('session') sessionId: string) { return { sessionId };}@Depends(provider: Provider, scope?: Scope)
Sección titulada «@Depends(provider: Provider, scope?: Scope)»Inyecta dependencias.
class DatabaseService { async getUsers() { return []; }}
@Controller('/users')class UserController { @Get('/') async list(@Depends(DatabaseService) db: DatabaseService) { return await db.getUsers(); }}Combinando Decoradores
Sección titulada «Combinando Decoradores»Múltiples Decoradores de Parámetro
Sección titulada «Múltiples Decoradores de Parámetro»@Post('/users/:id/posts')async createPost( @Param('id') userId: string, @Body(PostSchema) post: Post, @Header('authorization') auth: string, @Depends(DatabaseService) db: DatabaseService) { // Todos los parámetros son extraídos y validados return await db.createPost(userId, post);}Mejores Prácticas
Sección titulada «Mejores Prácticas»1. Siempre Usa Esquemas para Validación
Sección titulada «1. Siempre Usa Esquemas para Validación»// ✓ Buenoconst UserSchema = z.object({ name: z.string().min(2), email: z.string().email(),});
@Post('/users')async createUser(@Body(UserSchema) user: z.infer<typeof UserSchema>) { return user;}
// ✗ Malo@Post('/users')async createUser(@Body() user: any) { return user;}2. Usa Inferencia de Tipos
Sección titulada «2. Usa Inferencia de Tipos»// ✓ Buenoconst UserSchema = z.object({ name: z.string(), email: z.string().email(),});
type User = z.infer<typeof UserSchema>;
@Post('/users')async createUser(@Body(UserSchema) user: User) { // user está completamente tipado}3. Organiza los Controladores Lógicamente
Sección titulada «3. Organiza los Controladores Lógicamente»// ✓ Bueno - Un controlador por recurso@Controller('/users')class UserController { // Todas las rutas relacionadas con usuarios}
@Controller('/posts')class PostController { // Todas las rutas relacionadas con posts}4. Usa Inyección de Dependencias
Sección titulada «4. Usa Inyección de Dependencias»// ✓ Buenoclass UserService { async getUsers() { return []; }}
@Controller('/users')class UserController { @Get('/') async list(@Depends(UserService) userService: UserService) { return await userService.getUsers(); }}5. Maneja Errores Apropiadamente
Sección titulada «5. Maneja Errores Apropiadamente»@Get('/users/:id')async getUser(@Param('id') id: string) { const user = await db.getUserById(id);
if (!user) { throw new HTTPException(404, 'Usuario no encontrado'); }
return user;}Próximos Pasos
Sección titulada «Próximos Pasos»- Aprende sobre Inyección de Dependencias
- Explora Plugins
- Revisa la Guía de Inicio