Express.js Series: Deep Dive into the app.js Configuration

If you're like me and you start with something new, you want to know what every little piece does and means. This was the first problem I had with Express coming from just a basic JavaScript background.

After installing express and express-generator from npm and running npm install, you can generate your first Express app.

$ express myApp

The file I'm covering is app.js, the main configuration file for your Express app. When I first opened app.js it confused me. I will save you the trouble of doing research and just cover them here.

Before you do anything add the following to your app.js

app.listen(3000);

You need that in order to be able to actual open your app in the browser. Go to 127.0.0.1:3000 after you've started your app (using node app.js)

The Requires

The first thing you'll see in app.js are the requires. The require function is pretty straight forward. It's a built-in Node function that imports an object (module.exports) from another file or module.

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
 
var routes = require('./routes/index');
var users = require('./routes/users');

express - This one is quite obvious. It imports the framework into your app.

path - is a core Node module for working with and handling paths.

A good example is the path.join() method. This will normalise all the arguments into a path string. This can come in really handy using the __dirname global and a folder / file.

app.set('views', path.join(__dirname, 'views'));

This will set your apps view folder to something like:
/Users/jilles/Project/myApp/views

The path module won't actually check if it's an existing path. It's mainly used to transform path strings.

serve-favicon is Express middleware for serving a favicon. The advantage over just a normal favicon.ico on your public/ directory is that this module also deals with cache control. You can remove this module if you like because Express doesn't depend on it.

morgan is Express middleware for logging requests and responses. I like to use it during development and only during development so you can see what requests are being made. You can also remove this module without any consequences.

cookie-parser is Express middleware that helps you with handling cookies. Your request object will have a cookies object which you can acces use in your app. If your app doesn't use cookies you can leave it out.

body-parser is Express middleware you probably want to use if you're doing anything with forms. It will add a body object to your request so that you can access POST parameters.

routes / users are two dummy pages to show you how routing works. You could do all the routing in app.js but it will get messy as your application gets bigger.

That's it for the requires! You can always add more yourself or leave out some that you don't like or need.

A View Engine

If you've worked with templates before this will be a piece of cake. Don't worry if you haven't, it's still a piece of cake!

These 2 lines set the views folder and the view engine.

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

We've already discussed the path.join() part so by now you now that it tells Express to use the /views folder in your app directory.

The second app.set() tells Express to use the Jade templating engine. To give you an idea how Jade looks take a look at the image below taken from the Jade website.

It simplifies your HTML files and gives you conditionals which are really handy. You can choose not to render the login section when a user is already logged in for example.

Instead of saving your files as .html, you'll now have to save them as .jade in your /views folder.

app.use()

This method tells the app to use the parameters you're giving it. This can be a function or a path and a function. The capabilities are beyond the scope of this blog post. If you're really curious check out the documentation.

I'll go over the default app.use() calls in app.js. You can probably tell what they do by now because we went over the requires.

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

logger('dev') logs the requests to the console as seen above. The dev parameter means it will log a lot of information about the request such as the method, status code and response time.

bodyParser.json() gives your app the ability to parse JSON. This is necessary for when you're sending data (which you probably will with a JavaScript application) in JSON format.

bodyParser.urlencoded({ extended: false }) allows your app to read data from URLs (GET requests). Extended is true by default but you'll need the querystring module.

As mentioned in requires, cookieParser() adds an cookie object to all the requests you get.

express.static(path.join(__dirname, 'public')) tells your app to use the /public directory where you store images, stylesheets and scripts.

Next up are two routing methods. You can see that they're different from the ones above because they have 2 parameters instead of one.

app.use('/', routes);
app.use('/users', users);

The first parameter is the path, the second one is the function to execute. We separate the routes from the app.js because we don't want our app to be one big mess. Separating files in Node makes use of module.exports. I'll get to that in a bit.

The Special 404 Error

In Express a 404 is not the result of an error but rather the app running out of options. Once the request doesn't match any of the routes, it will reach the following function.

app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

This will throw a new error (404) and pass it on the the app using next().

Error handlers

When you're developing an Express app, you're going to make a few mistakes. Just like in any other programming language / framework. However some errors are really useful to you because you're a developer but might also be useful to someone with bad intentions. That's why we don't want to print a "stack trace" error when we're in production.

if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});
 

The only difference between the two is the response.render.

res.render('error', {
	message: err.message,
	error: err
});

If we're in development mode (app.get('env') ) we do want to print the stack trace. In any other case we just want to show the user the error.

We're almost there!

module.exports

Remember that require function? That makes use of the module.exports! When you want to use some variables or functions from another file, you attach them to the module.exports.

For example, imagine you have a utils.js JavaScript like below.

var utils = {
  add: function (a, b) {
    return a + b;
  }
};
module.exports = utils;

Then you can do this in your app.js

var utils = require('./utils.js');
console.log('10 + 10 = ' + utils.add(10, 10));

I hope this will help you with Express, if you have any questions don't hesitate to leave a reply.