How come my views are not returned when exported from a file in my routes directory?

Problem

I'm just experimenting with basic routing in ExpressJS and so far I have two routes:

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

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

When defined like this, in my app.js, everything works fine, but when exported as below, from individual files in my routes subdirectory, I get the error message that a callback function was expected, but an Object undefined was returned.

index.js:

exports.index = function(req,res) {
  res.render('index');
});

pics.js

exports.pics = function(req, res) {
  res.render('pics');
};

app.js

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

app.get('/', routes.index);
app.get('/pics', routes.pics);

What am I doing wrong in the latter example to break everything?

Problem courtesy of: skovsgaard

Solution

The index route is working but your pics route isn't because you are trying to import it from index.js.

The route directory has index.js which means that if you do require('./route') you are actually doing require('./route/index'). This happens because index.js has a special meaning in Node.js.

So to get pics working you need to do:

app.get('/pics', require('./routes/pics').pics);

This can be really confusing and is a question that gets asked a lot on the IRC channel.

Solution courtesy of: Pickels

Discussion

require('./routes') only loads ./routes/index.js, not ./routes/pics.js. So routes.pics will be undefined.

Discussion courtesy of: robertklep

require() will try to load index.js.

Here is a small coffeescript snippet that you can paste (convert it to js) in index.js. It will autoload all your files(routes) in the current directory:

index.coffee

module.exports = (app) =>
  require("fs").readdirSync(__dirname).forEach (file) ->
    i = file.lastIndexOf('.')
    ext = if (i < 0) then '' else file.substr(i)
    if (ext == ".js" or ext == ".coffee") and file != "index"+ext
      require("./" + file)(app)

app.js

require("./routes")(app)

someRoutes.js

app.get('/', function(req,res) {
  res.render('index');
});
Discussion courtesy of: Jean-Philippe Leclerc

This recipe can be found in it's original form on Stack Over Flow.