Routing
Route definitions
A route definition ties a method and a path to a handler. Stone.js gives you a decorator per HTTP verb, a controller decorator for grouping, and an imperative equivalent for each, all producing the same route table.
#Verb decorators
Inside a controller (a class marked with @EventHandler(basePath)), each method binds to a verb and a sub-path. Paths combine: the controller's base path plus the method's path.
import { EventHandler, Get, Post, Put, Patch, Delete, Any, Match } from '@stone-js/router'
import { IncomingHttpEvent } from '@stone-js/http-core'
@EventHandler('/tasks')
export class TaskController {
@Get('/') list () { /* GET /tasks */ }
@Get('/:id') show (e: IncomingHttpEvent) { /* GET /tasks/:id */ }
@Post('/') create (e: IncomingHttpEvent) { /* POST /tasks */ }
@Put('/:id') replace (e: IncomingHttpEvent) { /* PUT /tasks/:id */ }
@Patch('/:id') update (e: IncomingHttpEvent) { /* PATCH /tasks/:id */ }
@Delete('/:id') remove (e: IncomingHttpEvent) { /* DELETE /tasks/:id */ }
@Any('/search') search (e: IncomingHttpEvent) { /* any method */ }
@Match(['GET', 'HEAD'], '/health') health () { /* specific methods */ }
}import { defineEventHandler, defineRoutes } from '@stone-js/router'
export const routes = defineRoutes([
[defineEventHandler(TaskController, 'list'), { path: '/tasks', method: 'GET' }],
[defineEventHandler(TaskController, 'show'), { path: '/tasks/:id', method: 'GET' }],
[defineEventHandler(TaskController, 'create'), { path: '/tasks', method: 'POST' }],
[defineEventHandler(TaskController, 'replace'), { path: '/tasks/:id', method: 'PUT' }],
[defineEventHandler(TaskController, 'update'), { path: '/tasks/:id', method: 'PATCH' }],
[defineEventHandler(TaskController, 'remove'), { path: '/tasks/:id', method: 'DELETE' }],
[defineEventHandler(TaskController, 'search'), { path: '/search', methods: ['GET', 'POST'] }]
])#The available decorators
| Decorator | Type | Description |
|---|---|---|
| @Get | (path, options?) | Bind a GET route. |
| @Post | (path, options?) | Bind a POST route. |
| @Put | (path, options?) | Bind a PUT route. |
| @Patch | (path, options?) | Bind a PATCH route. |
| @Delete | (path, options?) | Bind a DELETE route. |
| @Options | (path, options?) | Bind an OPTIONS route. |
| @Any | (path, options?) | Match every HTTP method. |
| @Match | (methods, path, options?) | Match a specific set of methods. |
| @EventHandler | (basePath, options?) | Mark a class as a controller with a shared base path. |
#Route options
Every verb decorator (and every entry of defineRoutes) accepts the same option object. The most common are below; later pages cover the rest in context.
| Option | Type | Description |
|---|---|---|
| name | string | A stable name for URL generation and links. |
| path* | string | string[] | The path pattern (imperative form; the decorator takes it as its first argument). |
| method / methods | HttpMethod | HttpMethod[] | The verb(s) matched. |
| middleware | MixedPipe[] | Middleware run on the way to this handler. |
| rules | Record<string, RegExp | string> | Constraints on path parameters. |
| bindings | Record<string, Resolver> | Resolve a parameter into a model. |
| defaults | Record<string, unknown> | Default values for optional parameters. |
| redirect | string | object | Redirect instead of handling. |
| prefix | string | false | This route's relation to the router's global prefix: unset inherits it, false escapes it, a string replaces it. Per-route wins, like strict. |
| response | { type?, status?, headers? } | What the route answers with. The handler returns the payload; the framework builds the response. |
#Declaring what a route answers with
A route already says what it is: its path, its verb, what it accepts, whether it is protected. response is the other half of that sentence, so the handler can stay about the domain and there is one place to read when you want to know what an endpoint returns.
@Post('/tasks', { response: { type: 'json', status: 201 } })
create (event: IncomingHttpEvent): Task {
return this.tasks.add(event.get('body')) // the payload, nothing else
}
@Delete('/tasks/:id', { response: { type: 'no-content' } })
remove (event: IncomingHttpEvent): void { this.tasks.remove(event.get('id')) }| key | Type | Default | Description |
|---|---|---|---|
| type | 'json' | 'jsonp' | 'html' | 'text' | 'file' | 'redirect' | 'no-content' | 'json' | What kind of answer it is. |
| status | number | 200 | The status a successful answer carries. 302 for a redirect, 204 for no content. |
| headers | Record<string, string> | · | Headers to send with it. |