This package is intended for use with HTTP libraries that want to configure routes using ESNext decorators or a builder pattern.
With npm
:
npm install awilix-router-core
Or with yarn
yarn add awilix-router-core
The end-user of the routing library will be able to use decorators or a builder pattern to declaratively set up their routes, middleware and methods.
Note: in the examples below, an ES6 default
export is used, but named exports and multiple exports
per file are supported.
// You may re-export these as well.
import { route, before, GET, verbs, HttpVerbs } from 'awilix-router-core'
import bodyParser from 'your-framework-body-parser'
import authenticate from 'your-framework-authentication'
@before(bodyParser())
@route('/news')
export default class NewsController {
constructor({ service }) {
this.service = service
}
@GET()
async find(ctx) {
ctx.body = await this.service.doSomethingAsync()
}
@route('/:id')
@GET()
async get(ctx) {
ctx.body = await this.service.getNewsOrWhateverAsync(ctx.params.id)
}
@route('(/:id)')
@verbs([HttpVerbs.POST, HttpVerbs.PUT])
@before(authenticate())
async save(ctx) {
ctx.body = await this.service.saveNews(ctx.params.id, ctx.request.body)
}
}
// You may re-export these as well.
import { createController } from 'awilix-router-core'
import bodyParser from 'your-framework-body-parser'
import authenticate from 'your-framework-authentication'
// Can use a factory function or a class.
const api = ({ service }) => ({
find: async () => (ctx.body = await service.doSomethingAsync()),
get: async (ctx) =>
(ctx.body = await service.getNewsOrWhateverAsync(ctx.params.id)),
save: async (ctx) =>
(ctx.body = await service.saveNews(ctx.params.id, ctx.request.body)),
})
export default createController(api)
.before(bodyParser())
.prefix('/news')
.get('', 'find') // <- "find" is the method on the result from `api`
.get('/:id', 'get') // <- "get" is the method on the result from `api`
.verbs([HttpVerbs.POST, HttpVerbs.PUT], '/:id', 'save', {
// "save" is the method on the result from `api`
before: [authenticate()],
})
The framework adapter will use the tools provided by this package to extract routing config from decorated classes and register it in the router of choice.
Check out the awilix-koa
reference implementation, as well as the API docs here.
As mentioned earlier, this package exposes the user-facing route declaration API, as well as utilities needed for framework adapter authors.
There are 2 flavors of route declaration: builder and ESNext decorators.
The builder API's public top level exports are:
import { createController, HttpVerbs } from 'awilix-router-core'
Creates a controller that will invoke methods on an instance of the specified targetClassOrFunction
.
The controller exposes the following builder methods:
.get|post|put|patch|delete|head|options|connect|all(path, method, opts)
: shorthands for.verbs([HttpVerbs.POST], ...)
- seeHttpVerbs
for possible values..verbs(verbs, path, method, opts)
: registers a path mapping for the specified controller method..prefix(path)
: registers a prefix for the controller. Calling this multiple times adds multiple prefix options..before(middlewares)
: registers one or more middlewares that runs before any of the routes are processed..after(middlewares)
: registers one or more middlewares that runs after the routes are processed.
The optional opts
object passed to .verbs
can have the following properties:
before
: one or more middleware that runs before the route handler.after
: one or more middleware that runs after the route handler.
Note: all builder methods returns a new builder - this means the builder is immutable! This allows you to have a common builder setup that you can reuse for multiple controllers.
If you have enabled decorator support in your transpiler, you can use the decorator API.
The decorator API exports are:
import {
route,
before,
after,
verbs,
HttpVerbs,
// The following are just shortcuts for `verbs([HttpVerbs..])`
GET,
HEAD,
POST,
PUT,
DELETE,
CONNECT,
OPTIONS,
PATCH,
ALL,
} from 'awilix-router-core'
Class-level: adds a prefix to all routes in this controller.
Method-level: adds a route for the decorated method in the controller.
Has no effect if no verbs
are configured.
Example:
@route('/todos')
class Controller {
// GET /todos
// POST /todos
@GET()
@POST()
method1() {}
// PATCH /todos/:id
@route('/:id')
@PATCH()
method2() {}
}
Class-level: adds middleware to run before/after the routes are processed.
Method-level: adds middleware to run before/after the decorated method is processed.
Example:
@before([bodyParser()])
class Controller {
@before([authenticate()])
@after([compress()])
method() {}
}
Class-level: not allowed.
Method-level: adds HTTP verbs that the route will match.
Has no effect if no route
s are configured.
Example:
@verbs([HttpVerbs.GET, HttpVerbs.POST])
method() {}
GET
, POST
, etc.
Example:
@route('/todos')
class Controller {
// GET /todos
// POST /todos
@GET()
@POST()
method1() {}
// PATCH /todos/:id
@route('/:id')
@PATCH()
method2() {}
}
This section is for framework adapter authors. Please see awilix-koa for a reference implementation. If you need any help, please feel free to reach out!
The primary functions needed for this are getStateAndTarget
, rollUpState
, and findControllers
.
NOTE: when referring to "state-target tuple", it means an object containing
state
andtarget
properties, wheretarget
is the class/function to build up (usingcontainer.build
) in order to get an object to call methods on.
import {
getStateAndTarget,
rollUpState,
findControllers,
} from 'awilix-router-core'
Given a controller (either from createController
or a decorated class), returns a state-target tuple.
This will return a map where the key is the controller method name and the value is the routing config to set up for that method, with root paths + middleware stacks pre-merged.
Using fast-glob
, loads controllers from matched files.
Note: This uses
require
and so currently is not compatible with ESM.
Returns an array of state-target tuples.
Jeff Hansen — @Jeffijoe