293

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.

nbro
  • 15,395
  • 32
  • 113
  • 196
rafidude
  • 4,496
  • 7
  • 27
  • 23

13 Answers13

463

If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:

module.exports = function(app){

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

    //other routes..
}

And then you can require it from app.js passing the app object in this way:

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

Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation

ggorlen
  • 44,755
  • 7
  • 76
  • 106
BFil
  • 12,966
  • 3
  • 44
  • 48
  • 28
    Actually, the author (TJ Holowaychuck) gives a better approche: http://vimeo.com/56166857 – avetisk Mar 23 '13 at 09:36
  • Solves the routing issue for multiple files, but functions defined in app.js are not accessible in routes. – XIMRX May 12 '14 at 12:04
  • 7
    If you need some functions just put them into another module/file and require it from both app.js and routes.js – BFil May 12 '14 at 12:08
  • 4
    I understood everything hear but require('./routes')(app) this syntex blow my mind,can anybody tell me what is this exactly, or what is the use of this as far as i know its passing app object "app" – Rishabh Agrawal Dec 17 '15 at 12:41
  • Dear All, This is perfectly working but when using other routes like this : `require('./routes/route')(app); // pass our application into our routes` `require('./routes/dashboard')(app); // pass our application into our routes` .... Then second dashboard route isn't working at all ... what do to anyone please help ? – Amulya Kashyap May 17 '16 at 10:32
  • 7
    There is a better answer to this question below — http://stackoverflow.com/a/37309212/297939 – Dmitrii Dushkin Aug 01 '16 at 08:19
  • What @Dimitry said. But I would add that it's a cleaner solution – Michael Guild Aug 30 '17 at 22:04
  • 2
    @RishabhAgrawal It looks more complicated than it is. The `require('./routes')` part will import the function that was created in a separate file. The `(app)` part will call that function with `app` as the argument. Importing the function as `const routes = require('./routes')`and then calling it as `routes(app)` might be easier to understand. – Samuel Lindblom Jul 24 '21 at 13:09
255

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;
ggorlen
  • 44,755
  • 7
  • 76
  • 106
ZapRowsdower910
  • 3,024
  • 1
  • 17
  • 14
109

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);
Kostia
  • 6,284
  • 1
  • 18
  • 15
Sam Corder
  • 5,374
  • 3
  • 25
  • 30
  • 1
    i like this approach better, allows to add new routes without having to add anything specific to the main app file. – Jason Miesionczek Sep 08 '11 at 04:00
  • 3
    Nice, I use this approach as well, **with an additional check of the file extension** as I have faced issues with swp files. – Geekfish Jan 26 '12 at 23:53
  • You also don't have to use readdirSync w/ this, readdir works fine. – Paul May 22 '12 at 17:09
  • 6
    Is there any overhead in using this method to read the files in the directory vs. just requiring the routes in your app.js file? – Abadaba Nov 29 '12 at 03:08
  • I'd also like to know the same as @Abadaba. When does this get evaluated, when you launch the server or on every request? – imns Mar 07 '13 at 00:02
  • @bababa I would imagine when you launch the server. This line will probably execute once "require('./routes')(app);" – MikeMurko May 17 '13 at 16:31
  • Just to confirm, Node's require() function caches modules. So once something has been required once, it is not actually loaded and re-executed again. The index.js method illustrated above is a perfectly legitimate and performant way to include all files in a folder without worrying about updating your includes every time you create a new file. – ssafejava Sep 13 '13 at 09:29
  • Can you please be a bit more clear I don't seem to get this to work. Do you have any project you can link to? – Pablo Jomer Aug 08 '14 at 17:40
  • and after this we must equal ` require(./routes)(app) ` in some variable as routes and call in use method like this ` app.use('./v1' , routes ) ` is it correct ? – hamidreza nikoonia Mar 27 '19 at 21:04
  • @sam-corder You got me!! Better approach – Loki May 16 '19 at 12:01
  • Isn't this approach to auto import files a security risk? didn't Notepad++ have this issue with the NSA zero day where Notepad++ just blindly imported modules? – Elliot Huffman Jun 04 '21 at 03:02
47

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.

Alex
  • 3,719
  • 7
  • 35
  • 57
gihanchanuka
  • 4,783
  • 2
  • 32
  • 32
22

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' });
    });
}
Will
  • 24,082
  • 14
  • 97
  • 108
infinity1975
  • 403
  • 1
  • 5
  • 10
  • Thanks for this awesome answer. Helped me a lot. When implementing, I realized (1) you don't need the `'./'` when requiring since you're using path. So just `require(fullName)(app);` and (2) you need to add this in the beginning `if (file === 'index.js') return false;` or else the function will call indefinitely. – wongz Apr 13 '21 at 02:17
21

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);
    });
}
Brad Proctor
  • 543
  • 6
  • 11
  • Thanks for this. I had someone on a Mac adding `.DS_Store` files and it was messing everything up. – Jay Nov 23 '12 at 21:04
9

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

server.js:

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;

const app = new express();

//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));

//...fire connection
mongoose.connect(db.url, (err, database) => {
  if (err) return console.log(err);

  //...fire the routes
  app.use('/', routes);

  app.listen(port, () => {
    console.log('we are live on ' + port);
  });
});

/src/routes/index.js:

const express = require('express');
const app = express();

const siswaRoute = require('./siswa_route');

app.get('/', (req, res) => {
  res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);

module.exports = app;

/src/routes/siswa_route.js:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.json({item: 'Siswa page...'});
});

module.exports = app;
ggorlen
  • 44,755
  • 7
  • 76
  • 106
Sukma Saputra
  • 1,539
  • 17
  • 32
6

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} `);

}
NiallJG
  • 1,881
  • 19
  • 22
  • 1
    +1. This is the approach shown in the official example here: https://github.com/strongloop/express/tree/master/examples/route-separation – Matt Browne May 19 '15 at 19:40
  • 1
    Does this work for sharing global functions and variables under app.js? Or would you have to "pass" them into `wf.foo`, etc. since they're out of scope as with the other presented solutions? I'm referring to the case where normally you'd access shared variables/functions in wf.foo if it wasn't separated out of app.js. – David Mar 23 '16 at 02:12
  • yes it does , if you declare the 'foo' function in app.js then app.get('/wf', foo); will work – NiallJG Mar 23 '16 at 02:18
5

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

regretoverflow
  • 2,093
  • 1
  • 23
  • 45
5

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.

Mike S.
  • 969
  • 5
  • 13
3

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/books' => {name: 'Dreams from My Father by Barack Obama'}
ttemple
  • 1,825
  • 2
  • 17
  • 12
1

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.

Evan Burbidge
  • 827
  • 9
  • 16
0

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}]
};