--- name: koapp-router description: Define routes, path parameters, validators, and nested routers with the @axiosleo/koapp Router class. Use when declaring HTTP/WebSocket/TCP routes in koapp, adding path params like /users/{:id}, composing nested routers, attaching per-route middlewares or after-handlers, validating params/query/body with validatorjs rules, or using shortcut helpers (get/post/put/patch/delete/any). --- # @axiosleo/koapp Router Source: [`src/router.js`](../../../src/router.js). `Router` is the single route-definition primitive for all three application types (`KoaApplication`, `SocketApplication`, `WebSocketApplication`). A router can be flat or a tree of sub-routers, and it carries middlewares, handlers, after-handlers, and validators. ## Constructor ```javascript const { Router } = require('@axiosleo/koapp'); const router = new Router(prefix, options); ``` | Arg | Type | Default | Notes | | --- | --- | --- | --- | | `prefix` | `string` | `''` | Path prefix like `/api/v1` or `/users/{:id}` | | `options.method` | `string` | `''` | HTTP method. Use `'any'` for all methods. Empty string disables the route | | `options.handlers` | `Function[]` | `[]` | Async handler functions `async (context) => { ... }` | | `options.middlewares` | `Function[]` | `[]` | Run before handlers | | `options.afters` | `Function[]` | `[]` | Run after handlers (even on thrown response) | | `options.validators` | `object` | `{}` | `{ params, query, body }` - each `{ rules, messages? }` | Any unknown keys on `options` are kept on the router instance as-is. ## Path params Use `{:name}` for path parameters. Legacy `:name` also works but `{:name}` is preferred: ```javascript new Router('/users/{:id}/posts/{:postId}', { method: 'GET', handlers: [async (context) => { const { id, postId } = context.params; }] }); ``` Wildcards: - `/*` - matches one segment (captured as `params.` if named) - `/**` - matches one segment without capture - `/***` - matches any remaining path, ideal for fallback handlers ## HTTP method shortcuts Each method shortcut creates a nested sub-router: ```javascript const root = new Router('/api'); root.get('/users', listUsers); root.post('/users', createUser, { body: { rules: { name: 'required|string', email: 'required|email' } } }); root.put('/users/{:id}', updateUser); root.patch('/users/{:id}', patchUser); root.delete('/users/{:id}', deleteUser); root.any('/health', healthcheck); ``` Under the hood every shortcut delegates to `router.push(method, prefix, handler, validator)`. ## Custom methods ```javascript root.push('copy', '/resources/{:id}', async (context) => { ... }); ``` The framework uppercases and stores the method; match it by sending the same method string. ## Combining multiple methods ```javascript root.push('get|post', '/multi', async (context) => { // handles both GET and POST }); ``` ## Nested routers ```javascript const api = new Router('/api'); const v1 = new Router('/v1'); const users = new Router('/users'); users.get('/{:id}', async (context) => { ... }); v1.add(users); // /api/v1/users/{:id} api.add(v1); ``` `router.add(...subrouters)` attaches other Router instances. If the first argument is a string prefix, sub-routers are added under it: ```javascript api.add('/admin', adminUserRouter, adminRoleRouter); ``` The helper `router.new(prefix, options)` creates and mounts a sub-router in one call: ```javascript api.new('/internal', { middlewares: [authMiddleware] }); ``` ## Middlewares and afters ```javascript const root = new Router(null, { middlewares: [ async (context) => { console.log(`[${context.method}] ${context.pathinfo}`); } ], afters: [ async (context) => { console.log('response:', context.response); } ] }); ``` - Middlewares inherit down the tree (children + grandchildren see them) - Afters also inherit and run after **any** handler in the subtree - Throwing inside a handler still runs afters ## Request validation Validation uses [validatorjs](https://github.com/mikeerickson/validatorjs) rules. ```javascript root.new('/items/{:id}', { method: 'PUT', handlers: [updateItem], validators: { params: { rules: { id: 'required|integer' } }, query: { rules: { include: 'in:profile,settings' } }, body: { rules: { name: 'required|string', price: 'required|numeric|min:0' }, messages: { required: 'The :attribute field is required.' } } } }); ``` On failure the framework throws `HttpError(400, )` which the workflow converts into a `400` response. Common validatorjs rules: `required`, `string`, `integer`, `numeric`, `email`, `url`, `min:N`, `max:N`, `in:a,b,c`, `regex:/pattern/`, `boolean`, `array`. ## The catch-all fallback To handle anything that did not match: ```javascript root.any('/***', async (context) => { result('Not Found', 404, { 'Content-Type': 'text/plain' }); }); ``` `'/***'` matches any remaining path. The framework falls back to the deepest matching `/***` route when no exact match is found. ## Method resolution order For a request `PUT /users/42`: 1. Walk the router tree by path segments, preferring static matches over `*` / `**` / `***` 2. When multiple candidates remain, prefer the one whose `method` list includes the request method (or `ANY`) 3. If nothing matches, follow the nearest `/***` fallback 4. If even that misses, the framework lets the next Koa middleware run (typically 404) ## Common pitfalls - Leaving `method: ''` (or unset) on a `router.push` creates a route that matches no HTTP method - requests fall through to fallback handlers. Always use `'any'` or a concrete method unless you know you want this. - `new Router('/')` and `new Router('')` behave differently; prefer `null` or `''` for the root. - Path params must use `{:name}` inside route segments - `{:name}` **must be the entire segment**, not a substring (`/a-{:x}` does not work). - Validators bubble with the router; nested routers inherit parent middlewares and afters, so avoid double-registering auth middlewares. ## See also - Richer, copy-paste examples: [examples.md](examples.md) - Building a complete HTTP server around the router: **koapp-apps** - Sending responses from handlers: **koapp-response** - Typed contexts and Router generics in TypeScript: **koapp-typescript**