Controllers
A controller is a class annotated with @DController(path). Methods inside it become handlers when annotated with one of @DGet / @DPost / @DPut / @DPatch / @DDelete.
import {
DController,
DGet,
DPost,
DPut,
DPatch,
DDelete,
DPath,
DBody,
} from '@routup/decorators';
@DController('/users')
export class UserController {
@DGet('') async list() {}
@DGet('/:id') async show(@DPath('id') id: string) {}
@DPost('') async create(@DBody() body: any) {}
@DPut('/:id') async replace(@DPath('id') id: string) {}
@DPatch('/:id') async patch(@DPath('id') id: string) {}
@DDelete('/:id') async remove(@DPath('id') id: string) {}
}The full path is controllerPath + methodPath, so the show handler above resolves to GET /users/:id.
Multiple mount paths
@DController also accepts an array of paths. The controller is mounted under each path, so every method is reachable through any of them:
@DController(['/users', '/members'])
export class UserController {
@DGet('') async list() {}
@DGet('/:id') async show(@DPath('id') id: string) {}
}GET /users and GET /members both resolve to list; GET /users/42 and GET /members/42 both resolve to show. This mirrors @trapi/core's controller paths field, so the OpenAPI generator picks up every mount point.
Returning values
In routup v5, handlers return values directly — no send(res, data). The return value is converted to a Response by the core (see routup's response model):
@DGet('')
async list() {
return [{ id: 1 }, { id: 2 }]; // → application/json
}
@DGet('/raw')
async raw() {
return 'hello'; // → text/plain
}
@DGet('/file')
async file() {
return new Response(buffer, { headers: { 'content-type': 'image/png' } });
}Accessing the event
Use @DContext() when you need the full IAppEvent (e.g. to set event.response.status, read event.headers, mutate event.store):
import type { IAppEvent } from 'routup';
import { DContext, DController, DPost } from '@routup/decorators';
@DController('/orders')
export class OrderController {
@DPost('')
async create(@DContext() event: IAppEvent) {
event.response.status = 201;
return { id: '...' };
}
}Async handlers
All method decorators support async handlers; the plugin awaits the returned promise before passing the result to the response pipeline.
Multiple controllers
Pass an array to decorators({ controllers: [...] }). Each controller's @DController(path) becomes a sub-mount:
router.use(decorators({
controllers: [UserController, OrderController, AuthController],
// ...
}));See also
- Parameters — every
@D*parameter decorator - Overview — installation, tsconfig, and full app wiring