Code Monkey home page Code Monkey logo

koa-routing's Introduction

koa-routing

Build Status Dependency Status

Installation

npm install koa-routing

Motivation

I wanted to separate my route definitions into multiple files. Also I wanted to make easier to specify route handlers, and execute some methods before some set of routes, for example ensuring that user is authenticated before doing some action. So I developed koa-r and koa-routing to achieve that. Final result is something like this:

/routing/index.js file

module.exports = function (app) {
  require('./users')(app.route('/api/users').before(authenticate));
};

/routing/users.js file

/**
 * /api/users
 */

module.exports = function (route) {
  /* GET /api/users */
  route.get(r('user', 'getUsers'));

  /* GET /api/users/logout */
  route.nested('/logout').get(r('user', 'logout'));
};

So here you can see that we are specifying handlers for route with r('module', 'method') pattern, and we are also following DRY principle when we define our routes.

If you like this idea, you are on right place.

Example

Let's define following routes:

  • /users [GET, POST, PUT],
  • /users/list [GET, PUT]

With koa-routing you can nest routes, and on that way you can follow DRY principle. Also koa-routing architecture help you to separate route handlers into multiple files. That example will be shown also.

var koa = require('koa'),
	routing = require('koa-routing');

var app = koa();
app.use(routing(app));

app.route('/users')
  .get(function * (next) {
    this.body = 'from get';
    yield next;
  })
  .post(function * (next) {
    this.body = 'from post';
    yield next;
  })
  .put(function * (next) {
    this.body = 'from put';
    yield next;
  })
  .nested('/list')
    .get(function * (next) {
      this.body = 'from users list GET';
      yield next;
    });
    .put(function * (next) {
      this.body = 'from users list PUT';
      yield next;
    });

app.listen(4000);

You should put koa-routing middleware after body parsers and simmilar middlewares which are preparing request for you, or passing an options object with a defer field setted to true.

As you can see, you can pass classic express route style, such as /user/:id, and after that you can read received values from this.params or this.request.params object.

You can pass also regex as route path.

API

route

koa-routing extends you application instance with route method. You can use that method for defining route path.

app.route('/users/:id');

HTTP methods

After you define your route, you need set HTTP methods for that route. In following example you need to replace someHTTPmethod with one of supported node HTTP methods. That can be GET, POST, PUT, etc...

app.route('route path').someHTTPmethod(handler);

So you can type something like:

var handler = function * () {
  yield next;
};

app.route('api/users').get(handler);

Keep in mind that every call returns router instance, so everything can be chained.

nested

Let's we say that you have for routes something like this:

  • /api/users/profile/data
  • /api/users/profile/image
  • etc.

You see that you are repeating /api/users/profile for every route, and we don't want to do that. koa-routing have nice solution for this with nested function.

// first you type fixed part
var route = app.route('/api/users/profile');

route.nested('/data')
  .get(function * (next) { yield next; });
  // here you can also define other HTTP operations, like POST, PUT, etc
  // example of put...
  .put(function * (next) { yield next; });

route.nested('/image')
  .get(function * (next) { yield next; });

Keep in mind that nested creates new route for you and returns created route. You can continue nesting routes. It is up to you.

before

You can define function which will be executed before each route method, and before all nested routes.

app.route('/someRoute')
	.before(function * (next) {
		this.status = 300;
	})
	.get(function * (next) {
		this.body = 'should not be here';
		this.status = 200;
		yield next;
	});

all

This function will be executed if there is no matching HTTP method.

app.route('/someRoute')
	.all(function * (next) {
		this.body = 'will catch GET/POST/PUT... etc';
		this.status = 200;
		yield next;
	})

Other features

Multiple middlewares

With koa-routing you can provide multiple middlewares for each route method:

app.route('/multipleMiddleware')
	.get(function * (next) {
		this.body = '1';
		this.status = 200;
		yield next;
	}, function * (next) {
		this.body = '2';
		yield next;
	});

If you go to this route you will receive 2 as a result, because request will be passed to each defined handler.

Options

app.use(routing(app,options));
  • defer Default is false. If true, serves after yield next, allowing any downstream middleware to respond first.

Contributing

Feel free to send pull request with some new awesome feature or some bug fix. But please provide some tests with your contribution.

License

MIT

koa-routing's People

Contributors

ivpusic avatar randing89 avatar seonpiao avatar xesrevinu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

koa-routing's Issues

npm version

Can you update the version of NPM? Thank you.

Nested routes Not Found

app.route('/dept')
.get(function *(){
  //return a list of departments
})
.nested(/^(?:\/((?!\/).*))?\/manager\/?/)
.get(function *(){
  //return a list of department managers, optionally limited to a particular department
});

produces a 404 on GET /dept/manager
produces a 404 on GET /dept//manager
produces a 404 on GET /dept/sales/manager
produces a 204 on GET /dept

So it correctly matches the /dept route, but not the nested route.

Can't apply `before`-middleware to nested routes

When I stumbled upon this library, I was hoping that I would be able to use the .before method to gate off entire routing subtrees behind a middleware function.

For example, I want to ensure a user is signed in for route /me and any nested route /me/foo, /me/foo/bar, etc. And your API looked like a clean way to do such a thing.

I would have expected this to work:

function *ensureCurrUser(next) {
  console.log('Ensuring a user is logged in');
  yield next;
}

app.route('/')
  .nested('me')
    .before(ensureCurrUser)
    .get(function *() { this.body = ':)' });

Navigating to /me will display ":)" but the ensureCurrUser will not be called.

However, the .before trigger will fire when I place it directly on the route('/'):

app.route('/')
  .before(ensureCurrUser)
  .nested('me')
    .get(function *() { this.body = ':)' });

I was hoping that I would be able to describe all routes in one single call, merely cascading my middleware down the routing subtree.

app.route('/')
  .nested('me')
    .before(ensureCurrUser)  // All routes below this can assume a user is logged in
    .get(function *() { this.body = ':)' })
    .nested('/foo')
      .before(loadFoo)  // All routes below this can assume that `foo` is loaded
      .get(...)
      .nested('bar') 
        .get(...);

Any advice?

Before not working?

var clientRoute = app.route('/client/:client')
clientRoute.before(apiResponse, parseParameters, lookupClient);

clientRoute.nested('/conversions').get(conversions.list);
clientRoute.nested('/displays').get(displays.list);
clientRoute.nested('/price-breakdown').get(priceBreakdown.list);
clientRoute.nested('/ota-availability').get(priceBreakdown.availablity);
clientRoute.nested('/ota-breakdown').get(perfBreakdown.list);
clientRoute.nested('/property-breakdown').get(propertyBreakdown.list);

This doesn't work. Also tried declaring before seperately - result: my middlewares are not executed. When doing it like this:
clientRoute.nested('/displays').get(apiResponse, parseParameters, lookupClient, displays.list);
It works just fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.