DI Decorators — API Reference
@tagna/udiot v1.0.0-rc.1
All DI decorators require
emitDecoratorMetadata: trueandexperimentalDecorators: trueintsconfig.json, andimport 'reflect-metadata'at your application entry point.
@Injectable(options?)
Target: class
Marks a class as injectable. The decorator reads TypeScript's emitted constructor parameter types and stores them as InjectableMetadata so the container can resolve dependencies automatically.
function Injectable(options?: InjectableOptions): ClassDecorator
InjectableOptions
interface InjectableOptions {
scope?: ServiceScope; // Default: ServiceScope.SINGLETON
providedIn?: string | 'root';
}
| Option | Description |
|---|---|
scope |
Lifecycle of instances — SINGLETON, TRANSIENT, or SCOPED. |
providedIn |
When set to 'root', the class is automatically registered in globalContainer on first resolve(). |
import { Injectable, ServiceScope } from '@tagna/udiot';
@Injectable()
class UserService {
constructor(private db: DatabaseService) {}
}
@Injectable({ scope: ServiceScope.TRANSIENT })
class RequestLogger {
log(msg: string): void { console.log(msg); }
}
@Injectable({ scope: ServiceScope.SINGLETON, providedIn: 'root' })
class ConfigService {
readonly apiUrl = process.env.API_URL ?? 'http://localhost:3000';
}
@Inject(token)
Target: constructor parameter
Overrides the automatically inferred token for a single constructor argument. Required when injecting string tokens, Symbol tokens, or when emitDecoratorMetadata cannot infer the type correctly (e.g., interfaces, which are erased at runtime).
function Inject(token: InjectionToken): ParameterDecorator
import { Injectable, Inject } from '@tagna/udiot';
const API_KEY = Symbol('ApiKey');
@Injectable()
class ApiClient {
constructor(
@Inject('API_URL') private apiUrl: string,
@Inject(API_KEY) private apiKey: string,
private logger: LoggerService // type-based, no @Inject needed
) {}
}
container.registerValue('API_URL', 'https://api.example.com');
container.registerValue(API_KEY, process.env.API_KEY!);
@InjectProperty(token?)
Target: class property
Marks a property for injection after instance construction. The container resolves the property's type (via design:type metadata) if no token is supplied, or uses the explicit token if provided.
function InjectProperty(token?: InjectionToken): PropertyDecorator
import { Injectable, InjectProperty } from '@tagna/udiot';
@Injectable()
class OrderService {
@InjectProperty()
private userService: UserService; // resolved by type
@InjectProperty('CURRENCY_CODE')
private currencyCode: string; // resolved by string token
processOrder(id: string): void {
const user = this.userService.findById(id);
// ...
}
}
Property injection is useful for breaking circular dependencies, since the property is populated after the constructor runs.
@Optional()
Target: constructor parameter
Allows a dependency to be absent. If the token is not registered, undefined is injected instead of throwing ProviderNotFoundError.
function Optional(): ParameterDecorator
import { Injectable, Inject, Optional } from '@tagna/udiot';
@Injectable()
class AuditService {
constructor(
@Optional() @Inject('AUDIT_LOGGER') private logger?: AuditLogger
) {}
record(event: string): void {
this.logger?.write(event);
}
}
@Optional() must appear before @Inject() when both are present on the same parameter.
@Component(options)
Target: class
Marks a class as a UI component. Internally applies @Injectable() and registers the component in ComponentRegistry using the provided selector.
function Component(options: ComponentOptions): ClassDecorator
ComponentOptions
interface ComponentOptions {
selector: string; // Required. E.g. 'app-user-card'
template?: string; // Inline HTML template
templateUrl?: string; // Path to external template file
styles?: string[]; // Inline CSS strings
styleUrls?: string[]; // Paths to CSS files
inputs?: string[]; // Declared input property names
outputs?: string[]; // Declared output event names
description?: string; // Documentation / AI context
tags?: string[]; // Categorization tags
aiGenerated?: boolean; // Whether this component was AI-generated
version?: string; // Component version (default '1.0.0')
author?: string; // Author name
}
import { ComponentDecorator } from '@tagna/udiot';
@ComponentDecorator({
selector: 'user-card',
template: '<div class="card">{{ user.name }}</div>',
styles: ['.card { border: 1px solid #ccc; padding: 16px; }'],
description: 'Displays a user summary card',
tags: ['user', 'card']
})
class UserCardComponent {
constructor(private userService: UserService) {}
}
@Controller(options)
Target: class
Marks a class as an HTTP controller. Applies @Injectable() and stores route metadata. HTTP method decorators (@Get, @Post, @Put, @Patch, @Delete) register individual route handlers on the controller.
function Controller(pathOrOptions?: string | ControllerOptions): ClassDecorator
ControllerOptions
interface ControllerOptions {
path?: string; // Base path prefix for all routes. E.g. '/api/users'
middleware?: any[]; // Middleware applied to all routes in this controller
}
import { Controller, Injectable } from '@tagna/udiot';
import { Get, Post, Body, Param } from '@tagna/udiot';
@Controller('/api/users')
class UsersController {
constructor(private userService: UserService) {}
@Get('/')
async listUsers() {
return this.userService.findAll();
}
@Get('/:id')
async getUser(@Param('id') id: string) {
return this.userService.findById(id);
}
@Post('/')
async createUser(@Body() dto: CreateUserDto) {
return this.userService.create(dto);
}
}
@Module(options)
Target: class
Defines a module that groups providers, controllers, and components. Modules are the primary unit of application organization.
function Module(options?: ModuleOptions): ClassDecorator
ModuleOptions
interface ModuleOptions {
imports?: any[]; // Other modules to import
providers?: any[]; // Services / providers to register
controllers?: any[]; // Controller classes
components?: any[]; // UI component classes
exports?: any[]; // Providers accessible to importing modules
}
import { Module } from '@tagna/udiot';
@Module({
imports: [DatabaseModule],
providers: [UserRepository, UserService],
controllers: [UsersController],
exports: [UserService]
})
class UserModule {}
Complete Example: UserModule
The example below wires UserRepository (singleton), UserService (depends on repository and a config token), and UserController into a @Module.
import {
Injectable,
Inject,
Controller,
Module,
ServiceScope
} from '@tagna/udiot';
import { Get, Post, Body, Param } from '@tagna/udiot';
import { globalContainer } from '@tagna/udiot';
// --- Config ---
globalContainer.registerValue('USER_SERVICE_CONFIG', {
maxPageSize: 100,
defaultRole: 'viewer'
});
// --- Repository ---
@Injectable({ scope: ServiceScope.SINGLETON })
class UserRepository {
private users: Map<string, User> = new Map();
findById(id: string): User | undefined {
return this.users.get(id);
}
findAll(): User[] {
return Array.from(this.users.values());
}
save(user: User): void {
this.users.set(user.id, user);
}
}
// --- Service Config Interface ---
interface UserServiceConfig {
maxPageSize: number;
defaultRole: string;
}
// --- Service ---
@Injectable({ scope: ServiceScope.SINGLETON })
class UserService {
constructor(
private repo: UserRepository,
@Inject('USER_SERVICE_CONFIG') private config: UserServiceConfig
) {}
findById(id: string): User | undefined {
return this.repo.findById(id);
}
findAll(page = 1): User[] {
const all = this.repo.findAll();
const pageSize = this.config.maxPageSize;
return all.slice((page - 1) * pageSize, page * pageSize);
}
create(data: CreateUserDto): User {
const user: User = {
id: crypto.randomUUID(),
role: this.config.defaultRole,
...data
};
this.repo.save(user);
return user;
}
}
// --- Controller ---
@Controller('/api/users')
class UserController {
constructor(private userService: UserService) {}
@Get('/')
listUsers() {
return this.userService.findAll();
}
@Get('/:id')
getUser(@Param('id') id: string) {
return this.userService.findById(id);
}
@Post('/')
createUser(@Body() dto: CreateUserDto) {
return this.userService.create(dto);
}
}
// --- Module ---
@Module({
providers: [UserRepository, UserService],
controllers: [UserController],
exports: [UserService]
})
class UserModule {}