39

I'm trying to figure out how to split my routes into separate files.

I have this so far, but it doesn't work. I just get Not found when I try to access http://localhost:3001/api/things

//server.js
var koa = require('koa');
var app = koa();
var router = require('koa-router');

app.use(router(app));
require('./routes')(app);


// routes.js
module.exports = function *(app){
  app.use('/api/things', require('./api/things'));
};


// api/things/index.js

var Router = require('koa-router');
var router = new Router({
  prefix: '/api/things'
});

router.get('/', function *(){
  this.body = [{ name: 'Foo'}, { name: 'Bar' }];
});

module.exports = router;
chovy
  • 72,281
  • 52
  • 227
  • 295

8 Answers8

74

EDIT: I've updated the code examples below because the koa-router package on npm is no longer maintained. The Koa team has made an official fork of it under the name @koa/router.


For anyone reading this, who is curious on how to do this in Koa 2.X:

app.js

import Koa from 'koa'
import rootRouter from './routes/root'
import userRouter from './routes/user'

const app = new Koa()

app.use(rootRouter.routes())
app.use(rootRouter.allowedMethods())
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())

export default app

routes/root.js

import Router from '@koa/router'
const router = new Router()

router.get('/', async (ctx, next) => {
  ctx.body = 'Hello'
})

export default router

routes/user.js

import Router from '@koa/router'
const router = new Router({ prefix: '/user' })

router.get('/', async (ctx, next) => {
  ctx.body = 'Some User'
})

export default router

If you want to avoid the repetition with the routes() and the allowedMethods(), you can use koa-compose to compose the middleware together. For simplicity, I made a wrapper around it to simplify working with koa-router. Using it would look something like this:

app.js

import Koa from 'koa'
import router from './routes'

const app = new Koa()

app.use(router())

export default app  

routes/index.js

import combineRouters from 'koa-combine-routers'
import rootRouter from './root'
import userRouter from './user'

const router = combineRouters(
  rootRouter,
  userRouter
)

export default router

And it would do the same thing.

Saad
  • 49,729
  • 21
  • 73
  • 112
  • I have tried this solution but I have the next error: Router is not defined. – nole Nov 23 '16 at 16:25
  • @nole. do you have a code sample you can share with something like [GitHub gist](http://gist.github.com)? – Saad Nov 25 '16 at 07:55
19

server.js

var app = require('koa')();
var router = require('./routes');
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);

routes.js

var router = require('koa-router')();
router.get('/', function* () {
    this.body = 'router test';
});
module.exports = router;
i10k
  • 191
  • 4
12

Something like this should work:

// app.js
var koa = require('koa'),
    app = koa();

require('./routes1')(app);
require('./routes2')(app);

app.listen(3000);

// routes1.js
var Router = require('koa-router');
function register (app) {
  var router = new Router({
    prefix: '/api'
  });
  router.get('/', ...); // responds to "/api"
  router.get('/messages', ...); // responds to "/api/messages"
  app.use(router.routes());
  app.use(router.allowedMethods());
}

module.exports = register

// routes2.js
var Router = require('koa-router');
function register (app) {
  var router = new Router();
  router.get('/', ...); // responds to "/"
  app.use(router.routes());
  app.use(router.allowedMethods());
}

module.exports = register
James Moore
  • 1,881
  • 14
  • 18
  • what is the purpose of the `allowedMethods()` call? – chovy May 18 '15 at 20:49
  • Supports http OPTIONS method and responds with something like this: 200 OK Allow: HEAD,GET,PUT,DELETE,OPTIONS Essentially specifying the methods the server supports for the given URL – James Moore May 18 '15 at 21:23
  • This is a great solution, but it is now outdated and will not work. – Travis Don Kindred Nov 22 '16 at 21:03
  • I forget the details. This is an old post. I believe there was a major update to koa or koa-router that changed the syntax. I ended up needing to do the following: var api = new Router(); var api1 = require('./methods/api1'); api.use('/api1/v0/', api1.routes(), api1.allowedMethods()); var api2 = require('./methods/api2'); api.use('/api2/v0/', api2.routes(), api2.allowedMethods()); app.use(api.routes()); – Travis Don Kindred Mar 29 '17 at 20:13
7

For Koa 2.x I find nested routes with prefixes to deliver the goods, with no additional packages.

Assuming /api/dogs and /api/cats are the required outcomes.

Like so:

app.js

import Koa from 'koa';
import Router from 'koa-router';
import apiRouter from './routes/apiRouter';

const app = new Koa();

app.use(apiRouter.routes(), apiRouter.allowedMethods());

app.listen(3000);

export default app;

routes/apiRouter.js

import Router from 'koa-router'

import dogsRouter from './dogsRouter'
import catsRouter from './catsRouter'

const apiRouter = new Router({ prefix: '/api' })

const nestedRoutes = [dogsRouter, catsRouter]
for (var router of nestedRoutes) {
    apiRouter.use(router.routes(), router.allowedMethods())
}

export default apiRouter;

routes/dogsRouter.js

import Router from 'koa-router'

const dogsRouter = new Router({ prefix: '/dogs' });

dogsRouter.get('/', async (ctx, next) => {
  ctx.body = 'Dogs be here'
});

export default dogsRouter;

routes/catsRouter.js

import Router from 'koa-router'

const catsRouter = new Router({ prefix: '/cats' });

catsRouter.get('/', async (ctx, next) => {
  ctx.body = 'Cats be here'
});

export default catsRouter;
Lavi Avigdor
  • 4,092
  • 3
  • 25
  • 28
4

Minimalistic approach which i think TJ kept in mind when he did

koa, koa-route, koa-mount

Approach with small independent apps which mounted the way you like then:

index.js

var app = require('./app')
app.listen(3000);

app.js

const Koa = require('koa')
const _ = require('koa-route')
const mount = require('koa-mount')
const app = new Koa()
const pets = require('./pets')

// sub apps
app.use(mount('/pets', pets))

// root app
app.use(_.get('/', function(){
  this.body = "hello";
}))

module.exports = app;

pets.js

var Koa = require('koa');
var _ = require('koa-route');
var app = new Koa();


app.use(_.get('/', ctx => ctx.body = "pets" ));
app.use(_.get('/:name', (ctx, name) => ctx.body = "pet: "+ name));

module.exports = app;
Vladimir Buskin
  • 614
  • 4
  • 8
3

Here's what I ended up going with:

//server.js
'use strict';

var koa = require('koa');
var app = koa();
var serve = require('koa-static');
var path = require('path');
var router = require('koa-router');
var cfg = require('./config');
var mw = require('./middleware');

app.use(serve(path.resolve(__dirname, '../client')));
app.use(mw.requestTime('Response-time'));
app.use(router(app));
app.use(cfg.db.connect);

require('./routes')(app);

app.get('/api', function *(){
  this.body = 'Welcome to API v1';
});

app.use(cfg.db.close);
app.listen(cfg.env.port);



//routes.js
module.exports = function (app){
  app.use(require('./api/things').routes());
};


// api/things/index.js
var Router = require('koa-router');

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

var ctrl = require('./controllers');

router.get('/', ctrl.list);
router.get('/:id', ctrl.get);
router.post('/', ctrl.post);
router.put('/:id', ctrl.put);
router.del('/:id', ctrl.del);

module.exports = router;


// api/things/controllers.js

var r = require('rethinkdb');
var http = require('http');
var parse = require('co-body');
var ctrl = module.exports = {};

ctrl.list = function *(next){
};

ctrl.get = function *(next){
};

ctrl.post = function *(next){
};

ctrl.put = function *(next){
};

ctrl.del = function *(next){
};
chovy
  • 72,281
  • 52
  • 227
  • 295
3

You can put all your routes inside a single folder called routes, then loop through all files in that folder, require them and init. That way for any new route, just create a file in routes folder and thats it.

So something like this:

index.js

const Koa = require("koa");
const logger = require("koa-logger");
const bodyParser = require("koa-bodyparser");

const app = new Koa();

app.use(logger());
app.use(bodyParser());

require("./routes")(app);

const server = app.listen(config.server.port);

module.exports = server;

/routes/index.js

const fs = require("fs");
const path = require("path");
const baseName = path.basename(module.filename);

const routes = fs
    .readdirSync(path.join(__dirname))
    .filter(
        file =>
            file.indexOf(".") !== 0 &&
            file !== baseName &&
            file.slice(-3) === ".js"
    )
    .map(file => require(path.join(__dirname, file)));

module.exports = function(app) {
    for (var router of routes) {
        app.use(router.routes());
    }
};

Then inside /routes folder you would have like

/routes/user.js

/routes/customer.js

which are all router exports such as

/routes/auth.js

const Router = require("koa-router");
const authControllers = require("../controllers/auth");

const {
    jwtAuth,
    login,
    register,
    forgotPassword,
    resetPassword,
    getAuthenticatedUser
} = authControllers;

const router = new Router({ prefix: "/auth" });

router.post("/register", register);
router.post("/login", login);
router.post("/forgot-password", forgotPassword);
router.post("/reset-password/:resetToken", resetPassword);
router.get("/profile", jwtAuth, getAuthenticatedUser);

module.exports = router;
Goran Jakovljevic
  • 2,714
  • 1
  • 31
  • 27
  • 1
    I don't know why this answer is underrated. This can typically used in large applications to omit redundancy. Simple and follows DRY Principle. Thank you @Goran. Do you mind sharing any other tips for building large Koa apps? – Karma Blackshaw Jun 10 '20 at 16:07
  • Hi! I think your solution is the most valuable and simple solution there is. Do you know if there are any downsides by using multiple files (thus creating multiple routers with each have his own routes) than using a single router with multiple routes in terms of performance? – nir shabi Jul 12 '22 at 09:23
0

Simular, but exactly what I needed was:

app.js

"use strict";
import Koa from 'koa';
const app = new Koa();

import Router from 'koa-router';
const router = new Router();

import mount from 'koa-mount';
import routes from "./router/index.mjs";

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

app.listen(3000, () => console.log("Listening on port: 3000"));

router/index.mjs

"use strict";
import Koa from 'koa';
const app = new Koa();

import Router from 'koa-router';
const router = new Router();

router.get('/', async (ctx, next) => {
    ctx.body = {success: true};
    await next();
});

app.use(router.routes());

export default app;