ExisJS

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:

src/http/server.ts
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:

src/http/admin/boundary.ts
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.

src/http/users/route.ts
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:

src/http/users/route.ts
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?) — Extracts req.body or 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 the next function.

4. Middleware & Pipeline Decorators

You can bind middleware, rate limiting, and idempotency to controllers or route methods using @Use() and utility decorators:

src/http/checkout/route.ts
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 the Idempotency-Key header using off-heap native memory.

5. Response Modifiers

Modify HTTP response status codes, redirect clients, or set headers declaratively:

src/http/api/route.ts
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:

src/http/users/service.ts
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:

src/http/users/route.ts
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)  }}