Controllers
Controllers provide a structured, class-based approach to defining routes using TypeScript decorators. This pattern is familiar to NestJS developers and helps organize your application.
Basic Controller
Section titled “Basic Controller”Create a controller using the @Get, @Post, etc. decorators:
import { Get, Post, Put, Delete, Param, Body } from 'shokupan';
export class UserController {
@Get('/') async getAllUsers() { return { users: ['Alice', 'Bob', 'Charlie'] }; }
@Get('/:id') async getUserById(@Param('id') id: string) { return { id, name: 'Alice', email: 'alice@example.com' }; }
@Post('/') async createUser(@Body() body: any) { return { message: 'User created', data: body }; }
@Put('/:id') async updateUser( @Param('id') id: string, @Body() body: any ) { return { message: 'User updated', id, data: body }; }
@Delete('/:id') async deleteUser(@Param('id') id: string) { return { message: 'User deleted', id }; }}Mounting Controllers
Section titled “Mounting Controllers”Mount a controller to a base path:
import { Shokupan } from 'shokupan';
const app = new Shokupan();
// Mount at /api/usersapp.mount('/api/users', UserController);
// Routes available:// GET /api/users// GET /api/users/:id// POST /api/users// PUT /api/users/:id// DELETE /api/users/:idHTTP Method Decorators
Section titled “HTTP Method Decorators”All HTTP methods are supported:
import { Get, Post, Put, Patch, Delete, Options, Head, All } from 'shokupan';
export class ApiController {
@Get('/resource') getResource() { }
@Post('/resource') createResource() { }
@Put('/resource/:id') replaceResource() { }
@Patch('/resource/:id') updateResource() { }
@Delete('/resource/:id') deleteResource() { }
@Options('/resource') resourceOptions() { }
@Head('/resource') resourceHead() { }
@All('/webhook') handleWebhook() { }}Parameter Decorators
Section titled “Parameter Decorators”Extract data from requests using parameter decorators:
@Param
Section titled “@Param”Extract path parameters:
@Get('/users/:userId/posts/:postId')getPost( @Param('userId') userId: string, @Param('postId') postId: string) { return { userId, postId };}@Query
Section titled “@Query”Extract query parameters:
@Get('/search')search( @Query('q') searchQuery: string, @Query('page') page?: string) { return { query: searchQuery, page: page || '1' };}Access the request body:
@Post('/users')async createUser(@Body() userData: any) { // userData is already parsed return { created: userData };}@Headers
Section titled “@Headers”Access request headers:
@Get('/protected')getData( @Headers('authorization') token: string, @Headers('user-agent') userAgent: string) { return { token, userAgent };}Access the full context object:
import { Ctx, ShokupanContext } from 'shokupan';
@Get('/info')getInfo(@Ctx() ctx: ShokupanContext) { return { method: ctx.method, path: ctx.path, headers: Object.fromEntries(ctx.headers.entries()) };}Access the request object directly:
import { Req } from 'shokupan';
@Post('/upload')async upload(@Req() req: Request) { const formData = await req.formData(); return { uploaded: true };}Controller Middleware
Section titled “Controller Middleware”Apply middleware to all routes in a controller using @Use:
import { Use } from 'shokupan';
const authMiddleware = async (ctx, next) => { if (!ctx.headers.get('authorization')) { return ctx.json({ error: 'Unauthorized' }, 401); } ctx.state.user = { id: '123' }; return next();};
@Use(authMiddleware)export class AdminController {
@Get('/dashboard') getDashboard(@Ctx() ctx: any) { return { user: ctx.state.user }; }
@Get('/settings') getSettings(@Ctx() ctx: any) { return { user: ctx.state.user }; }}Method-Level Middleware
Section titled “Method-Level Middleware”Apply middleware to specific methods:
const logRequest = async (ctx, next) => { console.log(`${ctx.method} ${ctx.path}`); return next();};
export class UserController {
@Get('/') getAllUsers() { return { users: [] }; }
@Post('/') @Use(logRequest) createUser(@Body() body: any) { return { created: body }; }}Dependency Injection
Section titled “Dependency Injection”Use the DI container in controllers:
import { Container } from 'shokupan';
class UserService { getUsers() { return ['Alice', 'Bob']; }}
Container.register('userService', UserService);
export class UserController { private userService: UserService;
constructor() { this.userService = Container.resolve('userService'); }
@Get('/') getAllUsers() { return { users: this.userService.getUsers() }; }}Multiple Controllers
Section titled “Multiple Controllers”Organize your app with multiple controllers:
export class UserController { @Get('/') getUsers() { }}
// post.controller.tsexport class PostController { @Get('/') getPosts() { }}
// app.tsimport { Shokupan } from 'shokupan';
const app = new Shokupan();
app.mount('/api/users', UserController);app.mount('/api/posts', PostController);TypeScript Configuration
Section titled “TypeScript Configuration”For controllers to work, enable decorators in tsconfig.json:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true }}Next Steps
Section titled “Next Steps”- Middleware - Create custom middleware
- Dependency Injection - Advanced DI patterns
- Validation - Validate controller inputs