Skill v1.0.1
currentAutomated scan100/100+2 new
version: "1.0.1" 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`.
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
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:
new Router('/users/{:id}/posts/{:postId}', {method: 'GET',handlers: [async (context) => {const { id, postId } = context.params;}]});
Wildcards:
/*- matches one segment (captured asparams.<next>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:
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
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
root.push('get|post', '/multi', async (context) => {// handles both GET and POST});
Nested routers
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:
api.add('/admin', adminUserRouter, adminRoleRouter);
The helper router.new(prefix, options) creates and mounts a sub-router in one call:
api.new('/internal', { middlewares: [authMiddleware] });
Middlewares and afters
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 rules.
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, <first-error-message>) 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:
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:
- Walk the router tree by path segments, preferring static matches over
*/**/*** - When multiple candidates remain, prefer the one whose
methodlist includes the request method (orANY) - If nothing matches, follow the nearest
/***fallback - If even that misses, the framework lets the next Koa middleware run (typically 404)
Common pitfalls
- Leaving
method: ''(or unset) on arouter.pushcreates 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('/')andnew Router('')behave differently; prefernull
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
- 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