Controllers
Controllers are responsible for handling incoming HTTP requests and returning responses. A controller groups related routes under a common path prefix.
Defining a controller
Use the @Controller() decorator to define a controller and its path prefix:
import type { RequestContext } from '@miiajs/core'
import { Controller, Get, Post } from '@miiajs/core'
@Controller('/users')
class UserController {
@Get('/')
list() {
return [{ id: 1, name: 'Alice' }]
}
@Get('/:id')
findOne(ctx: RequestContext) {
return { id: ctx.params.id }
}
@Post('/')
create(ctx: RequestContext) {
return ctx.body
}
}
Controllers must be registered in a Module to be discovered by the framework.
Route decorators
MiiaJS provides decorators for all standard HTTP methods:
| Decorator | HTTP Method |
|---|---|
@Get(path?) | GET |
@Post(path?) | POST |
@Put(path?) | PUT |
@Patch(path?) | PATCH |
@Delete(path?) | DELETE |
@Head(path?) | HEAD |
@Options(path?) | OPTIONS |
The path argument is optional and defaults to '' (the controller prefix itself).
RequestContext
Every route handler receives a RequestContext object as its first argument:
interface RequestContext {
req: Request // Native Fetch API Request
res: ResponseBuilder // Fluent response builder
params: Record<string, string> // URL path parameters
query: Record<string, string> // Parsed query string
rawQuery: URLSearchParams // Raw query for multi-value params
body: any // Parsed request body
state: Map<string, any> // Shared state between middleware
route?: RouteInfo // Matched route metadata
user?: any // User object (set by auth)
}
query, rawQuery, and state are lazy-loaded on first access for better performance.
Route parameters
Dynamic segments in the path are extracted as ctx.params:
@Get('/:userId/posts/:postId')
getPost(ctx: RequestContext) {
const { userId, postId } = ctx.params
return { userId, postId }
}
Response handling
Route handlers can return values in several ways:
Auto JSON
Return any object or array and it will be serialized as JSON with status 200:
@Get('/')
list() {
return [{ id: 1 }, { id: 2 }]
}
Custom status
Use the @Status() decorator to set a different status code:
import { Status } from '@miiajs/core'
@Post('/')
@Status(201)
create(ctx: RequestContext) {
return { id: 3, created: true }
}
Native Response
Return a Response object for full control:
@Get('/download')
download() {
return new Response('raw body', {
status: 200,
headers: { 'Content-Type': 'text/plain' },
})
}
Response builder
Use ctx.res for a fluent API:
@Get('/html')
page(ctx: RequestContext) {
ctx.res
.status(200)
.header('X-Request-Id', 'abc')
.html('<h1>Hello</h1>')
}
See Response for more details.
Validation
MiiaJS provides built-in validation decorators that work with any ZodLike schema (Zod, or any object with safeParse()):
import { ValidateBody, ValidateQuery, ValidateParams } from '@miiajs/core'
import { z } from 'zod'
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
const QuerySchema = z.object({
page: z.string().transform(Number).default('1'),
limit: z.string().transform(Number).default('10'),
})
@Controller('/users')
class UserController {
@Post('/')
@Status(201)
@ValidateBody(CreateUserSchema)
create(ctx: RequestContext) {
// ctx.body is validated and typed
return ctx.body
}
@Get('/')
@ValidateQuery(QuerySchema)
list(ctx: RequestContext) {
return { page: ctx.query.page, limit: ctx.query.limit }
}
}
Validation decorators throw UnprocessableException (422) with detailed error messages on failure.
| Decorator | Validates | Source |
|---|---|---|
@ValidateBody(schema) | Request body | ctx.body (auto-parsed from JSON) |
@ValidateQuery(schema) | Query parameters | ctx.query |
@ValidateParams(schema) | Path parameters | ctx.params |