Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

logoran-router

logoran27MIT1.0.5

Router middleware for logoran. Provides RESTful resource routing.

logoran, middleware, router, route

readme

logoran-router

NPM version NPM Downloads Node.js Version Build Status Tips Gitter Chat

Router middleware for logoran

  • Express-style routing using app.get, app.put, app.post, etc.
  • Named URL parameters.
  • Named routes with URL generation.
  • Responds to OPTIONS requests with allowed methods.
  • Support for 405 Method Not Allowed and 501 Not Implemented.
  • Multiple route middleware.
  • Multiple paths.
  • Multiple routers.
  • Nestable routers.
  • ES7 async/await support.

Migrating to 7 / Logoran 1

  • The API has changed to match the new promise-based middleware signature of logoran. See the logoran 1.x readme for more information.
  • Middleware is now always run in the order declared by .use() (or .get(), etc.), which matches Express 4 API.

Installation

Install using npm:

npm install logoran-router

API Reference

Router ⏏

Kind: Exported class

new Router([opts])

Create a new router.

Param Type Description
[opts] Object
[opts.prefix] String prefix router paths

Example
Basic usage:

var Logoran = require('logoran');
var Router = require('logoran-router');

var app = new Logoran();
var router = new Router();

router.get('/', (ctx, next) => {
  // ctx.router available
});

app
  .use(router.routes())
  .use(router.allowedMethods());

router.get|put|post|patch|delete|del ⇒ Router

Create router.verb() methods, where verb is one of the HTTP verbs such as router.get() or router.post().

Match URL patterns to callback functions or controller actions using router.verb(), where verb is one of the HTTP verbs such as router.get() or router.post().

Additionaly, router.all() can be used to match against all methods.

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) => {
    // ...
  });

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.

Query strings will not be considered when matching requests.

Named routes

Routes can optionally have names. This allows generation of URLs and easy renaming of URLs during development.

router.get('user', '/users/:id', (ctx, next) => {
 // ...
});

router.url('user', 3);
// => "/users/3"

Multiple middleware

Multiple middleware may be given:

router.get(
  '/users/:id',
  (ctx, next) => {
    return User.findOne(ctx.params.id).then(function(user) {
      ctx.user = user;
      next();
    });
  },
  ctx => {
    console.log(ctx.user);
    // => { id: 17, name: "Alex" }
  }
);

Multiple paths

Multiple paths may be given:

router.get(
  ['/users/:id', '/staffs/:id'],
  (ctx, next) => {
    return User.findOne(ctx.params.id).then(function(user) {
      ctx.user = user;
      next();
    });
  },
  ctx => {
    console.log(ctx.user);
    // => { id: 17, name: "Alex" }
  }
);

Nested routers

Nesting routers is supported:

var forums = new Router();
var posts = new Router();

posts.get('/', (ctx, next) => {...});
posts.get('/:pid', (ctx, next) => {...});
forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods());

// responds to "/forums/123/posts" and "/forums/123/posts/123"
app.use(forums.routes());

Router prefixes

Route paths can be prefixed at the router level:

var router = new Router({
  prefix: '/users'
});

router.get('/', ...); // responds to "/users"
router.get('/:id', ...); // responds to "/users/:id"

URL parameters

Named route parameters are captured and added to ctx.params.

router.get('/:category/:title', (ctx, next) => {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' }
});

The path-to-regexp module is used to convert paths to regular expressions.

Kind: instance property of Router

Param Type Description
path String
[middleware] function route middleware(s)
callback function route callback

router.routes ⇒ function

Returns router middleware which dispatches a route matching the request.

Kind: instance property of Router

router.use([path], 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.

Kind: instance method of Router

Param Type
[path] String
middleware function
[...] function

Example

// 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());

router.prefix(prefix) ⇒ Router

Set the path prefix for a Router instance that was already initialized.

Kind: instance method of Router

Param Type
prefix String

Example

router.prefix('/things/:thing_id')

router.allowedMethods([options]) ⇒ function

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.

Kind: instance method of Router

Param Type Description
[options] Object
[options.throw] Boolean throw error instead of setting status and header
[options.notImplemented] function throw the returned value in place of the default NotImplemented error
[options.methodNotAllowed] function throw the returned value in place of the default MethodNotAllowed error

Example

var Logoran = require('logoran');
var Router = require('logoran-router');

var app = new Logoran();
var router = new Router();

app.use(router.routes());
app.use(router.allowedMethods());

Example with Boom

var Logoran = require('logoran');
var Router = require('logoran-router');
var Boom = require('boom');

var app = new Logoran();
var router = new Router();

app.use(router.routes());
app.use(router.allowedMethods({
  throw: true,
  notImplemented: () => new Boom.notImplemented(),
  methodNotAllowed: () => new Boom.methodNotAllowed()
}));

router.redirect(source, destination, [code]) ⇒ Router

Redirect source to destination URL with optional 30x status code.

Both source and destination can be route names.

router.redirect('/login', 'sign-in');

This is equivalent to:

router.all('/login', ctx => {
  ctx.redirect('/sign-in');
  ctx.status = 301;
});

Kind: instance method of Router

Param Type Description
source String URL or route name.
destination String URL or route name.
[code] Number HTTP status code (default: 301).

router.route(name) ⇒ Layer | false

Lookup route with given name.

Kind: instance method of Router

Param Type
name String

router.url(name, params, [options]) ⇒ String | Error

Generate URL for route. Takes a route name and map of named params.

Kind: instance method of Router

Param Type Description
name String route name
params Object url parameters
[options] Object options parameter
[options.query] Object | String query options

Example

router.get('user', '/users/:id', (ctx, next) => {
  // ...
});

router.url('user', 3);
// => "/users/3"

router.url('user', { id: 3 });
// => "/users/3"

router.use((ctx, next) => {
  // redirect to named route
  ctx.redirect(ctx.router.url('sign-in'));
})

router.url('user', { id: 3 }, { query: { limit: 1 } });
// => "/users/3?limit=1"

router.url('user', { id: 3 }, { query: "limit=1" });
// => "/users/3?limit=1"

router.param(param, middleware) ⇒ Router

Run middleware for named route parameters. Useful for auto-loading or validation.

Kind: instance method of Router

Param Type
param String
middleware function

Example

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', ctx => {
    return ctx.user.getFriends().then(function(friends) {
      ctx.body = friends;
    });
  })
  // /users/3 => {"id": 3, "name": "Alex"}
  // /users/3/friends => [{"id": 4, "name": "TJ"}]

router.follow(factory) ⇒ Router

Run follow factory for middleware with matched. Useful for auto-loading or validation.

Kind: instance method of Router

Param Type
factory function

Example

router
  .follow((matched) => {
    // matched.path is the full path of url
    // matched.metheds is the allowed http methods
    // matched.name is the name of the request
    // matched.stack is the all middlewares
    return (ctx, next) => {
      ctx.user = users[ctx.params.user];
      if (!ctx.user) return ctx.status = 404;
      return next();
    }
    // or return middleware and position.
    // return [(ctx, next) => {
    //   ctx.user = users[ctx.params.user];
    //   if (!ctx.user) return ctx.status = 404;
    //   return next();
    // }, 0];
  })
  .get('/users/:user', ctx => {
    ctx.body = ctx.user;
  })
  .get('/users/:user/friends', ctx => {
    return ctx.user.getFriends().then(function(friends) {
      ctx.body = friends;
    });
  })
  // /users/3 => {"id": 3, "name": "Alex"}
  // /users/3/friends => [{"id": 4, "name": "TJ"}]

Router.url(path, params) ⇒ String

Generate URL from url pattern and given params.

Kind: static method of Router

Param Type Description
path String url pattern
params Object url parameters

Example

var url = Router.url('/users/:id', {id: 1});
// => "/users/1"

Contributing

Please submit all issues and pull requests to the logoran/router repository!

Tests

Run tests using npm test.

Support

If you have any problem or suggestion please open an issue here.

changelog

History

7.4.0

  • Fix router.url() for multiple nested routers #407
  • layer.name added to ctx at ctx.routerName during routing #412
  • Router.use() was erroneously settings (.*) as a prefix to all routers nested with .use that did not pass an explicit prefix string as the first argument. This resulted in routes being matched that should not have been, included the running of multiple route handlers in error. #369 and #410 include information on this issue.

7.3.0

  • Router#url() now accepts query parameters to add to generated urls #396

7.2.1

  • Respond to CORS preflights with 200, 0 length body #359

7.2.0

  • Fix a bug in Router#url and append Router object to ctx. #350
  • Adds _matchedRouteName to context #337
  • Respond to CORS preflights with 200, 0 length body #359

7.1.1

  • Fix bug where param handlers were run out of order #282

7.1.0

  • Backports: merge 5.4 work into the 7.x upstream. See 5.4.0 updates for more details.

7.0.1

  • Fix: allowedMethods should be ctx.method not this.method #215

7.0.0

  • The API has changed to match the new promise-based middleware signature of koa 2. See the koa 2.x readme for more information.
  • Middleware is now always run in the order declared by .use() (or .get(), etc.), which matches Express 4 API.

5.4.0

  • Expose matched route at ctx._matchedRoute.

5.3.0

  • Register multiple routes with array of paths #203.
  • Improved router.url() #143
  • Adds support for named routes and regular expressions #152
  • Add support for custom throw functions for 405 and 501 responses #206

5.2.3

  • Fix for middleware running twice when nesting routes #184

5.2.2

  • Register routes without params before those with params #183
  • Fix for allowed methods #182

5.2.0

  • Add support for async/await. Resolves #130.
  • Add support for array of paths by Router#use(). Resolves #175.
  • Inherit param middleware when nesting routers. Fixes #170.
  • Default router middleware without path to root. Fixes #161, #155, #156.
  • Run nested router middleware after parent's. Fixes #156.
  • Remove dependency on koa-compose.

5.1.1

  • Match routes in order they were defined. Fixes #131.

5.1.0

  • Support mounting router middleware at a given path.

5.0.1

  • Fix bug with missing parameters when nesting routers.

5.0.0

  • Remove confusing API for extending koa app with router methods. Router#use() does not have the same behavior as app#use().
  • Add support for nesting routes.
  • Remove support for regular expression routes to achieve nestable routers and enable future trie-based routing optimizations.

4.3.2

  • Do not send 405 if route matched but status is 404. Fixes #112, closes #114.

4.3.1

  • Do not run middleware if not yielded to by previous middleware. Fixes #115.

4.3.0

  • Add support for router prefixes.
  • Add MIT license.

4.2.0

  • Fixed issue with router middleware being applied even if no route was matched.
  • Router.url - new static method to generate url from url pattern and data

4.1.0

Private API changed to separate context parameter decoration from route matching. Router#match and Route#match are now pure functions that return an array of routes that match the URL path.

For modules using this private API that need to determine if a method and path match a route, route.methods must be checked against the routes returned from router.match():

var matchedRoute = router.match(path).filter(function (route) {
  return ~route.methods.indexOf(method);
}).shift();

4.0.0

405, 501, and OPTIONS response handling was moved into separate middleware router.allowedMethods(). This resolves incorrect 501 or 405 responses when using multiple routers.

Breaking changes

4.x is mostly backwards compatible with 3.x, except for the following:

  • Instantiating a router with new and app returns the router instance, whereas 3.x returns the router middleware. When creating a router in 4.x, the only time router middleware is returned is when creating using the Router(app) signature (with app and without new).