koa-branch-router
v1.0.9
Published
Radix tree like router middleware for koa
Downloads
1,614
Maintainers
Readme
Koa branch router
Koa branch router is a simple radix tree (-ish) router for koa.
Why?
- There seem to be no radix tree routers with nested router support (at the time of writing).
- Popular routers handle middlewares in an inconvenient way. See Middleware Routing.
Features
- Express-style routing using verbs like
router.get
,router.put
,router.post
, etc. - Nested routes and middlewares.
- Path params and wildcard capturing.
Usage
const Koa = require('koa');
const Router = require('koa-branch-router');
const app = new Koa();
const router = new Router();
router.get('/', (ctx) => {
ctx.body = 'Hello World!';
});
app.use(router.routes());
app.listen(9000);
Middleware Routing
Middlewares are only called if a handler registered in the same router matches agaist the path.
Note
Middlewares and Handlers are essentially the same except:
// treated as a middleware if added using .use()
router.use('/', middleware)
// treated as a handler if added using .all() or .get(), .post() etc
router.all('/', handler)
Routing Example
// the path `/users/42/images`
router
.use('/users', new Router()
.use(userMiddleware) // <-- not called
.get('/', listUsers)
.get('/:userId', getUser)
.use('/users/:userId/images', new Router()
.use(imageMiddleware) // <-- called
.get('/', listImages) // <-- called
.get('/:imageId', getImage);
If you dont want this behaviour, you should use a route fragment.
// the path `/users/42/images`
router
.use('/users', new Router.Fragment()
.use(userMiddleware)) // <-- called
// fragments don't add a routing boundary and
// behave like a middleware registered on the
// parent router.
.use('/users', new Router()
.use(userMiddleware) // <-- not called
.get('/', listUsers)
.get('/:userId', getUser)
.use('/users/:userId/images', new Router()
.use(imageMiddleware) // <-- called
.get('/', listImages) // <-- called
.get('/:imageId', getImage);
Nested Routers
You may nest routers.
const userRouter = new Router()
.use('/', listUsers) // becomes `/users/`
.use('/:id', getUser); // becomes `/users/:id`
const tokenRouter = new Router({ prefix: '/tokens' })
.use('/', listUsers) // becomes `/auth/tokens/`
.use('/:id', getUser); // becomes `/auth/tokens/:id`
const fileRouter = new Router()
.use('/files', listFiles) // remains `/files/`
.use('/files/:id', getFile)); // remains `/files/:id`
const rootRouter = router
.use('/users', userRouter)
.use('/auth', tokenRouter)
.use(fileRouter);
Path Matching
Static
router.all('/users', ...)
| Path | Match |
| ----------- | ----- |
| /users
| true |
| /users/42
| false |
Named Parameters
Named parameters like :name
match a single path segment delimited by /
router.all('/users/:name', ...)
| Path | Match | Captured Params |
| --------------------- | ----- | --------------------- |
| /users
| false | |
| /users/gwen
| true | { name: 'gwen' }
|
| /users/profile
| true | { name: 'profile' }
|
| /users/gwen/profile
| false | |
Wildcard
Catches paths starting with the provided path, and captures the rest until the end.
router.all('/users/*path', ...)
| Path | Match | Captured Params |
| --------------------- | ----- | --------------------------- |
| /users
| false | |
| /users/gwen
| true | { path: 'gwen' }
|
| /users/profile
| true | { path: 'profile' }
|
| /users/gwen/profile
| true | { path: 'gwen/profile' }
|
Wildcards can be used without capturing as well.
router.all('/users/*', ...)
Quirks
- Captured parameters are decoded using
decodeURIComponent
, whereas wildcard captures are not.
API
new Router([options])
Initialize a new router.
Options
| Option | Default | Description |
| ------------------------ | -------- | ----------------------------------- |
| options.prefix
| ''
| Router prefix |
| options.caseSensitive
| false
| Case sensitive paths |
| options.strict
| false
| Treat /foo
and /foo/
as different urls |
router.verb()
Registers handlers for path. Supported verbs are:
router
.get(path, ctx => ...)
.post(path, ctx => ...)
.patch(path, ctx => ...)
.put(path, ctx => ...)
.delete(path, ctx => ...)
.del(path, ctx => ...) // alias for `.delete()`
.all(path, ctx => ...); // matches all methods
You may pass multiple handlers.
router.get(
path,
(ctx, next) => ...,
(ctx) => ...);
router.use()
Registers middleware for path
router
.use(parseToken)
.use(authorize);
// or multiple
router.use(parseToken, authorize);
// or with path
router.use('/users', authUser);
router.routes()
Returns router middleware.
app.use(router.routes());
new Router.Fragment([options])
Initialize a new route fragment. Fragments don't add a routing boundary and behave like a middleware registered on the parent router. See Middleware Routing.
Accepts the same options as a router. See Router Options
The API is again similar to the router except that there is no .routes()
method. Only routers can be mounted on apps.
ctx.params
This object contains key-value pairs of named route parameters.
// GET /user/42
router.get('/user/:name', (ctx) => {
ctx.params.name // => '42'
});