Routing
Matching & precedence
When a cause arrives, the router finds the one route that answers it. Knowing how that choice is made, and how to make it unambiguous, is what keeps a growing route table predictable.
#How a route is chosen
Matching considers, in effect: the HTTP method, then the path pattern, with more specific patterns preferred over catch-alls, then any constraints (rules) that must hold, and finally host (domain) and scheme (protocol) if declared. A route that fails any of these is skipped, and the next candidate is tried.
| Criterion | Type | Description |
|---|---|---|
| method | exact | The verb must match (or the route must be @Any / list the method). |
| path specificity | static > param > catch-all | A literal segment beats :param, which beats :rest*. |
| rules | must pass | A parameter constraint that fails removes the route from contention. |
| domain / protocol | optional filter | Restrict a route to a host or scheme. |
| strict | boolean | When on, trailing-slash and case differences stop a match. |
#Keeping the table unambiguous
Order-independence is the goal: two routes should never both be able to match the same request. Use constraints to separate look-alike paths, and reserve catch-alls for genuine fallbacks.
@Get('/tasks/:id', { rules: { id: /\d+/ } }) // /tasks/42
@Get('/tasks/:slug') // /tasks/ship-the-docs
// The constraint keeps these two from ever competing for the same URL.#Domain & subdomain routing
A route can be scoped to a host, and the host can itself carry parameters. A subdomain parameter turns multi-tenancy into routing: capture the tenant from the host and read it on the event like any other value.
@Get('/', { domain: 'admin.example.com' }) // only on that exact host
@EventHandler('/', { domain: '{tenant}.example.com' }) // capture a subdomain
export class TenantController {
@Get('/dashboard')
dashboard (event: IncomingHttpEvent) {
const tenant = event.get<string>('tenant') // from the host, not the path
return this.tenants.dashboard(tenant)
}
}Host parameters take constraints too (rules), and a whole group can share a domain, so an admin subdomain and a tenant subdomain are two groups over the same routes.
#Scheme
@Post('/hook', { protocol: 'https' }) // only over https