https://github.com/idiocc/goa-router
The Router For Creating Middleware For Goa Apps.
https://github.com/idiocc/goa-router
Last synced: about 1 year ago
JSON representation
The Router For Creating Middleware For Goa Apps.
- Host: GitHub
- URL: https://github.com/idiocc/goa-router
- Owner: idiocc
- License: agpl-3.0
- Created: 2019-12-12T22:19:17.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2019-12-21T08:52:08.000Z (over 6 years ago)
- Last Synced: 2025-02-15T07:48:59.252Z (over 1 year ago)
- Language: JavaScript
- Homepage: https://www.idio.cc
- Size: 307 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: COPYING
Awesome Lists containing this project
README
# @goa/router
[](https://www.npmjs.com/package/@goa/router)
`@goa/router` is The Router For Creating Middleware For Goa Apps.
```sh
yarn add @goa/router
```
## Table Of Contents
- [Table Of Contents](#table-of-contents)
- [API](#api)
* [`Router`](#type-router)
* [RouterConfig](#type-routerconfig)
- [Verbs](#verbs)
- [Allowed Methods](#allowed-methods)
* [`AllowedMethodsOptions`](#type-allowedmethodsoptions)
- [Named Routes](#named-routes)
- [Multiple Middleware](#multiple-middleware)
- [Nested Routes](#nested-routes)
- [Router Prefixes](#router-prefixes)
- [URL Parameters](#url-parameters)
- [Copyright & License](#copyright--license)
## API
The package is available by importing its default class:
```js
import Router from '@goa/router'
```
The example below creates a really simple router that responds to the `GET /` and `POST /users/:uid` requests. Because of `allowedMethods`, it will also send a response to the `OPTIONS` request with the `allow` header.
ExampleOutput
```js
import rqt, { aqt } from 'rqt'
import Goa from '@goa/koa'
import Router from '@goa/router'
const goa = new Goa()
const router = new Router()
router
.get('/', (ctx) => {
ctx.body = `Hello world`
})
.post('/users/:uid', (ctx) => {
ctx.body = `You have edited the user ${ctx.params.uid}`
})
goa.use(router.routes())
goa.use(router.allowedMethods())
```
```sh
# GET /
Hello world
# POST /users/100
You have edited the user 100
# OPTIONS /
HEAD, GET
```
__`Router`__: Router For Goa Apps.
Name
Type & Description
constructor
new (opts?: !RouterConfig) => Router
Create a new router.
```js
import Goa from '@goa/koa'
import Router from '@goa/router'
const app = new Goa()
const router = new Router()
router.get('/', (ctx, next) => {
// ctx.router available
})
app
.use(router.routes())
.use(router.allowedMethods())
```
static url
(path: string, ...params: !Object[]) => string
Generate URL from url pattern and given `params`.
```js
const url = Router.url('/users/:id', { id: 1 })
// => "/users/1"
```
opts
!RouterConfig
Stored options passed to the Router constructor.
allowedMethods
(options: !AllowedMethodsOptions) => !Middleware
Returns separate middleware for responding to `OPTIONS` requests with
an `Allow` header containing the allowed methods, as well as responding
with `405 Method Not Allowed` and `501 Not Implemented` as appropriate.
```js
import Goa from '@goa/koa'
import Router from '@goa/router'
const app = new Goa()
const router = new Router()
app.use(router.routes())
app.use(router.allowedMethods())
```
param
(param: string, middleware: !Middleware) => !Router
Run middleware for named route parameters. Useful for auto-loading or validation.
```js
router
.param('user', (id, ctx, next) => {
ctx.user = users[id]
if (!ctx.user) return ctx.status = 404
return next()
})
.get('/users/:user', ctx => {
ctx.body = ctx.user
})
.get('/users/:user/friends', async ctx => {
ctx.body = await ctx.user.getFriends()
})
```
redirect
(source: string, destination: string, code?: number) => !Router
Redirect source to destination URL with optional 30x status code.
Both source and destination can be route names.
route
(name: string) => Layer
Lookup route with given name. If the route is not found, returns null.
url
(name: string, ...params: !Object[]) => (string | !Error)
Generate URL for route. Takes a route name and map of named `params`. If the route is not found, returns an error. The last argument can be an object with the `query` property.
```js
// To use urls, a named route should be created:
router.get('user', '/users/:id', (ctx, next) => {
// ...
})
```
Get the URL by passing a **simple** parameter
```js
router.url('user', 3)
// => "/users/3"
```
Get the URL by passing parameters in an **object**
```js
router.url('user', { id: 3 })
// => "/users/3"
```
Use the url method for **redirects** to named routes:
```js
router.use((ctx) => {
ctx.redirect(ctx.router.url('sign-in'))
})
```
Pass an **object query**:
```js
router.url('user', { id: 3 }, { query: { limit: 1 } })
// => "/users/3?limit=1"
```
Pass an already **serialised query**:
```js
router.url('user', { id: 3 }, { query: 'limit=1' })
// => "/users/3?limit=1"
```
use
(path: (string | !Array<string> | !Middleware), ...middleware: !Middleware[]) => !Router
Use given middleware.
Middleware run in the order they are defined by `.use()`. They are invoked
sequentially, requests start at the first middleware and work their way
"down" the middleware stack.
```js
// session middleware will run before authorize
router
.use(session())
.use(authorize())
// use middleware only with given path
router.use('/users', userAuth())
// or with an array of paths
router.use(['/users', '/admin'], userAuth())
app.use(router.routes())
```
prefix
(prefix: string) => !Router
Set the path prefix for a Router instance that was already initialized.
```js
router.prefix('/things/:thing_id')
```
middleware
routesalias
() => !Middleware
Returns router middleware which dispatches a route matching the request.
RouterConfig: Config for the router.
| Name | Type | Description |
| ---------- | ----------------------------- | --------------------------------------------------------------------------------------------- |
| methods | !Array<string> | The methods to serve.
Default `HEAD`, `OPTIONS`, `GET`, `PUT`, `PATCH`, `POST`, `DELETE`. |
| prefix | string | Prefix router paths. |
| routerPath | string | Custom routing path. |
## Verbs
Routes are assigned to the router by calling HTTP method verbs on the instance:
```js
router
.get('/', (ctx, next) => {
ctx.body = 'Hello World!'
})
.post('/users', (ctx, next) => {
// ...
})
.put('/users/:id', (ctx, next) => {
// ...
})
.del('/users/:id', (ctx, next) => {
// ...
})
.all('/users/:id', (ctx, next) => {
// ...
})
```
Additionally, `router.all()` can be used to match against all methods. `router.del()` is an alias for `router.delete()`.
When a route is matched, its path is available at `ctx._matchedRoute` and if named, the name is available at `ctx._matchedRouteName`.
Route paths will be translated to regular expressions using [path-to-regexp](https://github.com/pillarjs/path-to-regexp).
Query strings will not be considered when matching requests.
## Allowed Methods
The router can respond to the `OPTIONS` request with the `allow` header.
**Example with [Boom](https://github.com/hapijs/boom)**
```js
const app = new Goa()
const router = new Router()
app.use(router.routes())
app.use(router.allowedMethods({
throw: true,
notImplemented: () => new Boom.notImplemented(),
methodNotAllowed: () => new Boom.methodNotAllowed(),
}))
```
__`AllowedMethodsOptions`__: The options for the `allowedMethods` middleware generation.
| Name | Type | Description |
| ---------------- | --------------------- | -------------------------------------------------------------------------- |
| throw | boolean | Throw error instead of setting status and header. |
| notImplemented | () => !Error | Throw the returned value in place of the default `NotImplemented` error. |
| methodNotAllowed | () => !Error | Throw the returned value in place of the default `MethodNotAllowed` error. |
## Named Routes
Routes can optionally have names. This allows generation of URLs and easy renaming of URLs during development.
```js
router.get('user', '/users/:id', (ctx, next) => {
// ...
})
router.url('user', 3)
// => "/users/3"
```
## Multiple Middleware
Multiple middleware may be passed to the router.
```js
router.get(
'/users/:id',
async (ctx, next) => {
const user = await User.findOne(ctx.params.id)
ctx.user = user
await next()
},
ctx => {
console.log(ctx.user)
// => { id: 17, name: "Alex" }
}
)
```
## Nested Routes
It's possible to create a _Router_ instance, and then pass another _Router_ instance to its `.use` call to nest the two.
SourceOutput
```js
const forums = new Router()
const posts = new Router()
posts.get('/', (ctx) => {
ctx.body = ctx.params
})
posts.get('/:pid', (ctx) => {
ctx.body = ctx.params
})
forums.use('/forums/:fid/posts',
posts.routes(),
posts.allowedMethods())
goa.use(forums.routes())
```
```js
// GET /forums/123/posts
{ fid: '123' }
// GET /forums/123/posts/123
{ fid: '123', pid: '123' }
```
## Router Prefixes
Route paths can be prefixed at the router level.
```js
const router = new Router({
prefix: '/users',
})
router.get('/', (ctx) => {
// responds to "/users"
ctx.body = ctx.params
})
router.get('/:id', (ctx) => {
// responds to "/users/:id"
ctx.body = ctx.params
})
goa.use(router.routes())
```
```js
// Request /users
{}
// Request /users/123
{ id: '123' }
```
## URL Parameters
Named route parameters are captured and added to `ctx.params`.
```js
const router = new Router()
router.get('/:category/:title', (ctx) => {
// the params are exposed to the context.
ctx.body = ctx.params
})
goa.use(router.routes())
```
```js
// Request /programming/how-to-node
{ category: 'programming', title: 'how-to-node' }
```
## Copyright & License
GNU Affero General Public License v3.0
[Original Work](https://github.com/ZijianHe/koa-router) by Alexander C. Mingoia under MIT License found in [COPYING](https://github.com/ZijianHe/koa-router/blob/master/LICENSE).
There's also a fork in the [Koa org](https://github.com/koajs/router).
© Idio 2019