Decorators
ExisJS provides standard ECMAScript decorators for developers who prefer the Object-Oriented Programming (OOP) paradigm.
Because ExisJS does not rely on reflection libraries like reflect-metadata, these decorators are fast, lightweight, and memory-efficient.
All decorators can be imported directly from exisjs/decorators.
1. Application & Boundary Decorators
ExisJS allows you to configure your root application and directory-scoped boundaries using decorators.
@Server(config)
Used in your entry point (e.g. src/http/server.ts) to configure plugins and root providers in an OOP style:
import { Server } from 'exisjs/decorators'import type { App } from 'exisjs'
@Server({ providers: [['Logger', { useValue: console }]]})export default class RootServer { async onStart(app: App) { console.log('Server started') }}@Boundary(config)
Used inside any folder (e.g. src/http/admin/boundary.ts) to configure folder-scoped CORS, headers, exclusions, and DI providers:
import { Boundary } from 'exisjs/decorators'import type { Request, Response, Next, BoundaryContext } from 'exisjs/router'import { UnauthorizedError } from 'exisjs/error'
@Boundary({ cors: { origin: 'https://admin.yourdomain.com', credentials: true }, headers: { 'X-Admin-Api-Version': '2.0' }, exclude: ['/admin/health'],})export default class AdminBoundary { // Chain step auto-detected by (req, res, next) signature requireAuth(req: Request, res: Response, next: Next) { if (!req.headers['x-api-key']) { throw new UnauthorizedError('Missing API key') } next() }
// Request wrapper auto-detected by handle(ctx, next) async handle(ctx: BoundaryContext, next: Next) { const start = Date.now() const result = await next() ctx.res.setHeader('X-Response-Time', `${Date.now() - start}ms`) return result }}2. Controllers & Routing
To map a class to a route prefix, use the @Controller() decorator. HTTP method decorators then map class methods to specific endpoints.
import { Controller, Get, Post, Put, Patch, Delete } from 'exisjs/decorators'import { UserParamsSchema, CreateUserSchema } from './schema'
@Controller('/users')export default class UsersController { @Get('/') async listUsers() { return { users: [] } }
@Get('/:id', UserParamsSchema) async getUser() { return { id: 1 } }
@Post('/', CreateUserSchema) async createUser() { return { success: true } }
@Delete('/:id') async deleteUser() {}}Available Method Decorators
@Get(path?, schema?)@Post(path?, schema?)@Put(path?, schema?)@Patch(path?, schema?)@Delete(path?, schema?)@Options(path?, schema?)@Head(path?, schema?)@Connect(path?, schema?)@Trace(path?, schema?)@All(path?, schema?)
3. Request Parameters
Parameter decorators extract request properties and inject them directly into your method parameters:
import { Controller, Post, Body, Param, Headers, Req, Res } from 'exisjs/decorators'
@Controller('/users')export default class UsersController { @Post('/:id') async update( @Param('id') id: string, @Body() payload: any, @Headers('authorization') token: string, @Req() req: any, @Res() res: any ) { return { success: true, id, payload } }}Available Parameter Decorators
@Body(nameOrPipe?)— Extractsreq.bodyor runs through a validation schema/pipe.@Param(nameOrPipe?)— Extracts a route path param (e.g.:id).@Headers(nameOrPipe?)— Extracts a request header by name.@HostParam(nameOrPipe?)— Extracts a host subdomain parameter.@Req()— Injects the raw request object.@Res(options?)— Injects the response object (e.g.{ passthrough: true }).@Ip()— Injects the client IP address.@Next()— Injects thenextfunction.
4. Middleware & Pipeline Decorators
You can bind middleware, rate limiting, and idempotency to controllers or route methods using @Use() and utility decorators:
import { Controller, Post, Use, Idempotent } from 'exisjs/decorators'import { requireAuth } from '../../middleware/auth'
@Controller('/checkout')@Use(requireAuth) // Applies to all routes in this controllerexport default class CheckoutController { @Post('/') @Idempotent({ ttlMs: 86400000 }) // In-memory off-heap idempotency protection async processPayment() { return { success: true } }}Available Lifecycle & Middleware Decorators
@Use(...middlewares)— Attaches middleware functions to a class or method.@Idempotent(options?)— Caches responses based on theIdempotency-Keyheader using off-heap native memory.
5. Response Modifiers
Modify HTTP response status codes, redirect clients, or set headers declaratively:
import { Controller, Post, Get, HttpCode, Header, Redirect } from 'exisjs/decorators'
@Controller('/api')export default class ApiController { @Post('/create') @HttpCode(201) // Forces 201 Created @Header('X-Custom-Version', '2.0') async create() { return { success: true } }
@Get('/legacy') @Redirect('/new-api', 301) async legacy() {}}Available Response & Metadata Decorators
@HttpCode(code: number)— Sets the response HTTP status code.@Header(name: string, value: string)— Injects a response header.@Returns(schema)— Attaches response schema for OpenAPI/Swagger generation.@Redirect(url: string, statusCode?: number)— Redirects the client.@Permissions(...permissions: string[])— Attaches route permissions metadata.@Hosts(...hosts: string[])— Restricts route/controller to specific Host headers.
6. Dependency Injection
To mark a service class as an injectable provider for the IoC container, use the @Injectable() decorator:
import { Injectable } from 'exisjs/decorators'
@Injectable({ scope: 'singleton' })export class UserService { async fetchUser(id: string) { return { id, name: 'Alice' } }}Inside your route classes or controllers, inject services via constructor injection:
import { Controller, Get, Param } from 'exisjs/decorators'import { UserService } from './service'
@Controller('/users')export default class UsersController { constructor(private userService: UserService) {}
@Get('/:id') async getUser(@Param('id') id: string) { return this.userService.fetchUser(id) }}