Skip to content

Decoradores e Interfaces

Fang JS utiliza Decoradores e Interfaces para garantizar que tu código siga una arquitectura predecible. Su función principal es validar que tus clases tengan la estructura necesaria para integrarse con el motor de rutas y el sistema de contextos.

El decorador @Controller valida que tu clase sea un controlador legítimo. Para que un controlador sea válido en Fang JS, debe tener un método estático llamado register.

Este método es el encargado de recibir el RouteGroup y definir las rutas.

import { Controller, type RouteGroup } from "@fang-js/fang";
@Controller
export class UserController {
static register(group: RouteGroup) {
group.get("/", (ctx) => ctx.ok("Lista de usuarios"));
group.post("/create", (ctx) => ctx.created("Usuario creado"));
}
}

Al registrarse, Fang lanzará un log informativo indicando que el controlador ha sido vinculado correctamente.

El decorador @Service asegura que tu lógica de negocio esté preparada para recibir el Contexto. En Fang JS, la regla de oro es: el primer argumento de cualquier método de servicio debe ser ctx: Context. Este decorador se auxilia de la clase BaseService.

import { Service, Context } from "@fang-js/fang";
@Service
export class UserService extends BaseService {
async findAll(ctx: Context) {
// ctx está disponible para acceder a query, params, etc.
return { message: "Buscando datos..." };
}
}

Esta clase abstracta “obliga” a tus métodos a seguir la firma correcta, dándote autocompletado inmediato mientras escribes, esta clase debe extender a tu service.

import { BaseService, Context } from "@fang-js/fang";
// Tu editor te avisará si olvidas incluir 'ctx' como primer parámetro
export class PostService extends BaseService {
async getPosts(ctx: Context, limit: number) {
return [];
}
}

Seguridad de Tipos

Evita errores comunes como olvidar pasar el contexto a las capas inferiores.

IntelliSense

Autocompletado instantáneo al implementar métodos en tus servicios.

Logs de Registro

Seguimiento claro de qué controladores se han cargado durante el arranque.

// 1. Definimos el Servicio y heredamos de la clase BaseService
import { Service, BaseService, Context } from "@fang-js/fang";
@Service
export class AuthService extends BaseService {
async login(ctx: Context) {
const body = await ctx.body();
return { token: "secret_wolf_token" };
}
}
// 2. Definimos el Controlador con el método static register
import { Controller, RouteGroup } from "@fang-js/fang";
@Controller
export class AuthController {
static register(group: RouteGroup) {
const service = new AuthService();
group.post("/login", (ctx) => service.login(ctx));
}
}
// 3. Registro en la App
app.group("/auth", AuthController.register);

A diferencia del registro manual usando el decorador @Controller, el decorador @ReflectionController permite una arquitectura totalmente automatizada. Este decorador recibe un prefijo de ruta y marca la clase para que el motor de Autoloading de Fang JS la encuentre y registre automáticamente.

Para usarlo, define el prefijo de la ruta en el decorador y asegúrate de implementar el método estático register.

import { ReflectionController, type RouteGroup } from "@fang-js/fang";
import { PeopleService } from "./people-service.js";
@ReflectionController("/people")
export class PeopleController {
static register(group: RouteGroup) {
const peopleService = new PeopleService();
group.get("/", (ctx) => peopleService.getPeople(ctx));
}
}
  • Metadata: El decorador normaliza el path (asegurando que empiece con /) y lo guarda como metadatos ocultos en la clase.

  • Descubrimiento: Cuando llamas a autoloadControllers, Fang escanea tus archivos, busca estos metadatos y ejecuta el método register.

  • Aislamiento: Cada controlador se convierte en un grupo de rutas independiente, manteniendo tu main.ts limpio de importaciones masivas.

import { Fang } from "@fang-js/fang";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
const app = new Fang();
const __dirname = dirname(fileURLToPath(import.meta.url));
async function bootstrap() {
// Escanea la carpeta actual y registra PeopleController automáticamente bajo /people
await app.autoloadControllers(__dirname);
app.listen(3000);
}
bootstrap();