[node.js] How to include route handlers in multiple files in Express?

In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.

How can I get app.js to recognize other route handlers defined in wf.js file?

A simple require does not seem to work.

This question is related to node.js express

The answer is


I wrote a small plugin for doing this! got sick of writing the same code over and over.

https://www.npmjs.com/package/js-file-req

Hope it helps.


If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:

var wf = require(./routes/wf);

then,

app.get('/wf', wf.foo );

where .foo is some function declared in your wf.js file. e.g

// wf.js file 
exports.foo = function(req,res){

          console.log(` request object is ${req}, response object is ${res} `);

}

Full recursive routing of all .js files inside /routes folder, put this in app.js.

// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');

function recursiveRoutes(folderName) {
    fs.readdirSync(folderName).forEach(function(file) {

        var fullName = path.join(folderName, file);
        var stat = fs.lstatSync(fullName);

        if (stat.isDirectory()) {
            recursiveRoutes(fullName);
        } else if (file.toLowerCase().indexOf('.js')) {
            require('./' + fullName)(app);
            console.log("require('" + fullName + "')");
        }
    });
}
recursiveRoutes('routes'); // Initialize it

in /routes you put whatevername.js and initialize your routes like this:

module.exports = function(app) {
    app.get('/', function(req, res) {
        res.render('index', { title: 'index' });
    });

    app.get('/contactus', function(req, res) {
        res.render('contactus', { title: 'contactus' });
    });
}

And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)

var fs = require('fs');

module.exports = function(app) {
    fs.readdirSync(__dirname).forEach(function(file) {
        if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
            return;
        var name = file.substr(0, file.indexOf('.'));
        require('./' + name)(app);
    });
}

Even though this an older question I stumbled here looking for a solution to a similar issue. After trying some of the solutions here I ended up going a different direction and thought I would add my solution for anyone else who ends up here.

In express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy to maintain url paths. For example if I have a separate route file for my '/tests' endpoint already and want to add a new set of routes for '/tests/automated' I may want to break these '/automated' routes out into a another file to keep my '/test' file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.

Contents of ./app.js:

var express = require('express'),
    app = express();

var testRoutes = require('./routes/tests');

// Import my test routes into the path '/test'
app.use('/tests', testRoutes);

Contents of ./routes/tests.js

var express = require('express'),
    router = express.Router();

var automatedRoutes = require('./testRoutes/automated');

router
  // Add a binding to handle '/tests'
  .get('/', function(){
    // render the /tests view
  })

  // Import my automated routes into the path '/tests/automated'
  // This works because we're already within the '/tests' route so we're simply appending more routes to the '/tests' endpoint
  .use('/automated', automatedRoutes);
 
module.exports = router;

Contents of ./routes/testRoutes/automated.js:

var express = require('express'),
    router = express.Router();

router
   // Add a binding for '/tests/automated/'
  .get('/', function(){
    // render the /tests/automated view
  })

module.exports = router;

I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:

I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:

app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));

Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:

var express = require('express');
var router = express.Router();
var consign = require('consign');

// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
  .include('api')
  .into(router);

module.exports = router;

Everything worked as expected. Hope this helps someone.


One tweak to all of these answers:

var routes = fs.readdirSync('routes')
      .filter(function(v){
         return (/.js$/).test(v);
      });

Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js


This is possibly the most awesome stack overflow question/answer(s) ever. I love Sam's/Brad's solutions above. Thought I'd chime in with the async version that I implemented:

function loadRoutes(folder){
    if (!folder){
        folder = __dirname + '/routes/';
    }

    fs.readdir(folder, function(err, files){
        var l = files.length;
        for (var i = 0; i < l; i++){
            var file = files[i];
            fs.stat(file, function(err, stat){
                if (stat && stat.isDirectory()){
                    loadRoutes(folder + '/' + file + '/');
                } else {
                    var dot = file.lastIndexOf('.');
                    if (file.substr(dot + 1) === 'js'){
                        var name = file.substr(0, dot);

                        // I'm also passing argv here (from optimist)
                        // so that I can easily enable debugging for all
                        // routes.
                        require(folder + name)(app, argv);
                    }
                }
            });
        }
    });
}

My directory structure is a little different. I typically define routes in app.js (in the root directory of the project) by require-ing './routes'. Consequently, I'm skipping the check against index.js because I want to include that one as well.

EDIT: You can also put this in a function and call it recursively (I edited the example to show this) if you want to nest your routes in folders of arbitrary depth.


Building on @ShadowCloud 's example I was able to dynamically include all routes in a sub directory.

routes/index.js

var fs = require('fs');

module.exports = function(app){
    fs.readdirSync(__dirname).forEach(function(file) {
        if (file == "index.js") return;
        var name = file.substr(0, file.indexOf('.'));
        require('./' + name)(app);
    });
}

Then placing route files in the routes directory like so:

routes/test1.js

module.exports = function(app){

    app.get('/test1/', function(req, res){
        //...
    });

    //other routes..
}

Repeating that for as many times as I needed and then finally in app.js placing

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

you can put all route functions in other files(modules) , and link it to the main server file. in the main express file, add a function that will link the module to the server:

   function link_routes(app, route_collection){
       route_collection['get'].forEach(route => app.get(route.path, route.func));
       route_collection['post'].forEach(route => app.post(route.path, route.func));
       route_collection['delete'].forEach(route => app.delete(route.path, route.func));
       route_collection['put'].forEach(route => app.put(route.path, route.func));
   }

and call that function for each route model:

link_routes(app, require('./login.js'))

in the module files(for example - login.js file), define the functions as usual:

const login_screen = (req, res) => {
    res.sendFile(`${__dirname}/pages/login.html`);
};

const forgot_password = (req, res) => {
    console.log('we will reset the password here')
}

and export it with the request method as a key and the value is an array of objects, each with path and function keys.

module.exports = {
   get: [{path:'/',func:login_screen}, {...} ],
   post: [{path:'/login:forgotPassword', func:forgot_password}]
};   

index.js

const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);

routes/users.js

const router = global.router
router.get('/', (req, res) => {
    res.jsonp({name: 'John Smith'})
}

module.exports = router

routes/books.js

const router = global.router
router.get('/', (req, res) => {
    res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}

module.exports = router

if you have your server running local (http://localhost:3000) then

// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}

// Books
curl --request GET 'localhost:3000/users' => {name: 'Dreams from My Father by Barack Obama'}

I am trying to update this answer with "express": "^4.16.3". This answer is the similar as ShortRound1911.

server.js

_x000D_
_x000D_
const express = require('express');_x000D_
const mongoose = require('mongoose');_x000D_
const bodyParser = require('body-parser');_x000D_
const db = require('./src/config/db');_x000D_
const routes = require('./src/routes');_x000D_
const port = 3001;_x000D_
_x000D_
const app = new express();_x000D_
_x000D_
//...use body-parser_x000D_
app.use(bodyParser.urlencoded({ extended: true }));_x000D_
_x000D_
//...fire connection_x000D_
mongoose.connect(db.url, (err, database) => {_x000D_
  if (err) return console.log(err);_x000D_
_x000D_
  //...fire the routes_x000D_
  app.use('/', routes);_x000D_
_x000D_
  app.listen(port, () => {_x000D_
    console.log('we are live on ' + port);_x000D_
  });_x000D_
});
_x000D_
_x000D_
_x000D_

/src/routes/index.js

_x000D_
_x000D_
const express = require('express');_x000D_
const app = express();_x000D_
_x000D_
const siswaRoute = require('./siswa_route');_x000D_
_x000D_
app.get('/', (req, res) => {_x000D_
  res.json({item: 'Welcome ini separated page...'});_x000D_
})_x000D_
.use('/siswa', siswaRoute);_x000D_
_x000D_
module.exports = app;
_x000D_
_x000D_
_x000D_

/src/routes/siswa_route.js

_x000D_
_x000D_
const express = require('express');_x000D_
const app = express();_x000D_
_x000D_
app.get('/', (req, res) => {_x000D_
  res.json({item: 'Siswa page...'});_x000D_
});_x000D_
_x000D_
module.exports = app;
_x000D_
_x000D_
_x000D_

I hope this can help someone. Happy coding!


If you're using express-4.x with TypeScript and ES6, this would be the best template to use:

src/api/login.ts

import express, { Router, Request, Response } from "express";

const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
    try {
        res.send('OK');
    } catch (e) {
        res.status(500).send(e.toString());
    }
});

export default router;

src/app.ts

import express, { Request, Response } from "express";
import compression from "compression";  // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';

const app = express();

app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());

app.get('/public/hc', (req: Request, res: Response) => {
  res.send('OK');
});

app.use('/user', login);

app.listen(8080, () => {
    console.log("Press CTRL-C to stop\n");
});

Much cleaner than using var and module.exports.


Examples related to node.js

Hide Signs that Meteor.js was Used Querying date field in MongoDB with Mongoose SyntaxError: Cannot use import statement outside a module Server Discovery And Monitoring engine is deprecated How to fix ReferenceError: primordials is not defined in node UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac internal/modules/cjs/loader.js:582 throw err DeprecationWarning: Buffer() is deprecated due to security and usability issues when I move my script to another server Please run `npm cache clean`

Examples related to express

UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block jwt check if token expired Avoid "current URL string parser is deprecated" warning by setting useNewUrlParser to true MongoNetworkError: failed to connect to server [localhost:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017] npm notice created a lockfile as package-lock.json. You should commit this file Make Axios send cookies in its requests automatically What does body-parser do with express? SyntaxError: Unexpected token function - Async Await Nodejs Route.get() requires callback functions but got a "object Undefined" How to redirect to another page in node.js