Solid ExpressJS server

Steps to develop good ExpressJS based application server.

Imagine you are writing a NodeJS webserver using expressjs. What should you do to create a secure, robust, easy to debug and deploy server? Here are my steps.

package.json

Always create a package.json file, even if you do not intend to make the module public. This file will list all dependencies (rather than listing them manually in the README.md), can have script commands to test and run. Inside your folder run npm init and answer a couple of questions. If the server code is private and you want to avoid accidentally publishing it to the public registry, set the private property

1
2
3
4
{
...
"private": true
}

use configuration helper

To avoid messing with per-environment (local, unit testing, staging, production) settings, start using a helper config module right away. I recommend nconf or config.

At a very least, read the port from the environment first, otherwise use default

1
2
var app = express();
app.listen(process.env.PORT || 3000, function() { ... });

Hosting environments will pass the port to listen to via the environment variable, while in the local environment is ok to hardcode it.

automatically restart server during development

I use nodemon to start the server while developing it locally.

1
2
3
"scripts": {
"watch": "nodemon server.js"
}

While developing locally I use npm run watch. Anytime there is a file change or a crash, the server restarts automatically. This speeds up the development.

save exact dependency versions

I prefer avoiding fuzzy dependency versions, like '1.1.*', or '^2.0.1', instead prefer to save the exact version of the installed module's in the package.json. To always do this, create and set the following configuration in a new project's .npmrc file

save-exact=true

Here is my .npmrc file in my user folder

email=<my NPM email>
save-exact=true
fetch-retries=5
always-auth=true

advanced: clean up package.json

To make sure the package.json sets all the options and everything is written alphabetically, I recommend using grunt-nice-package. Even if you do not use grunt, you can still easily run it

npm install --save-dev grunty grunt-nice-package

Add the command to the scripts list

1
2
3
"scripts": {
"nice-package": "grunty grunt-nice-package nice-package"
}

Execute npm run nice-package and fix all the errors / warnings. You package.json surely looks nice now!

use a linter

To catch simple errors (like misspelled variables) in the static code without writing unit tests, install a linter, I recommend eslint - it is versatile, powerful and can be easily extended with custom rules.

npm install --save-dev eslint

Then create a file .eslintrc - and set the following starting JSON

1
2
3
4
5
6
7
8
9
{
"env": {
"browser": false,
"node": true
},
"rules": {
"quotes": [2, "single"]
}
}

You can read how to configure eslint and see the entire list of rules at [http://eslint.org/docs/rules/](http://eslint.org/docs/rules/. To run eslint create a new command the package.json scripts object - this allows to use shortcut name eslint

1
2
3
"scripts": {
"lint": "eslint server.js src/**/*.js"
}

If fixing every error is too difficult initially, at least make them warnings write in the source code, for example, to make unused variables a warning, rather than an error, use the comment

1
/* eslint no-unused-vars: 1 */

advanced: lint on each commit

To always lint before committing the code to avoid breaking the master, set up a pre-commit command.

npm install --save-dev pre-git

Then add all commands (only lint for now) to the package.json

1
"pre-commit": "npm run lint"

You can always skip the pre-commit hook by committing with -n flag.

configure continuous integration service

Use Codeship (fast, works with Github and BitBucket, 100 private builds each month) or CircleCI (very fast, works with Github, single build host for free, even for unlimited private builds) to build your project on each commit.

Add badges to the project's README file to show the current build status, see badges

deploy to a hosting from CI

Instead of manually pushing the code to the hosting service, configure the CI server to push automatically. All CI services worth their salt have nice and simple to configure integration with the major providers (AWS, Heroku, etc). Just enter the branch / your hosting API key / application name. Each commit will be tested and if the tests pass, deployed to the host. This is fine, at least for the master branch.

embed git commit id

It is very useful for debugging to embed the last commit id in the server. The server can print the commit id at startup, add it as a meta tag to the rendered HTML views and send with each exception to the crash reporting service. You can read how to pass the commit id from the CI to the hosting environment. Then you can embed the commit as a meta tag in each rendered view or pass to the crash reporting service Raygun.

setup crash reporting service

You MUST setup backend and client-side crash reporting service, like Sentry or Raygun. The configuration is very simple, and there are handlers for both global JavaScript exceptions and errors inside the ExpressJS middleware. You will discover how the software crashes in the way you have never suspected. If you need to configure Sentry server-side, you can use raven-express middleware.

I advise to configure separate environments for the development vs production, and blacklist the local development to avoid the noise of multiple local errors.

To verify that the crash reporting service is working and catches the errors as expected, you can include the crasher middleware. It throws an artificial synchronous and asynchronous exceptions whenever someone requests /api/crash route. This is extremely useful for quick confirmation that the errors thrown inside the middleware are reported, as are asynchronous global errors.

output pretty HTML

I strongly believe that the server should always generate pretty HTML output, any space savings between the minified and the original HTML text are negligible. Full indented HTML source is invaluable when looking through the page trying to understand the structure. To enable pretty HTML rendering in ExpressJS 4 just add the following after setting the render engine.

1
2
var app = express();
app.locals.pretty = true;

setup simple unit tests

It is important to start unit testing very quickly. I show how to test the ExpressJS server in this blog post.

secure the server against common attacks

Read this excellent series of blog posts and apply as many solutions to secure your server against injection / cross site and other types of attacks. The simplest solution that covers most of the recommended steps is to include the helmet middleware.

1
2
3
4
var express = require('express');
var helmet = require('helmet');
var app = express();
app.use(helmet());

Protect against stray API requests

This is for websites that use forms to submit data.

If someone steals the session cookie from a valid session, they can then execute API requests directly to your server. A good intro to these "cross site forgery" attacks can be found at Understanding CSRF

To protect yourself, you want to send something else besides the cookie with each API request to guarantee that it is coming from your own web page, called CSRF tokens.

We can use expressjs/csurf module to automatically create a token that we can verify when someone makes a POST request.

1
2
3
4
5
6
7
8
9
10
// this example uses cookies
app.use(cookieParser())
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
})
// validate the incoming form has valid CSRF token field
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed')
})

The form in the send view has the csrf token value in a hidden input field

1
2
3
4
5
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>

End to end testing

I like unit testing Express but like end 2 end testing the server even more. Cypress.io is an excellent and powerful tool that works quickly right away. To start the server and then run the tests I use a utility to run NPM script commands in parallel.

1
npm install --save-dev npm-run-all

The script commands (see actual example in this repo).

1
2
3
4
5
6
7
{
"scripts": {
"test": "run-p --race start e2e",
"start": "node server.js",
"e2e": "cypress run"
}
}

Related

More information about making an ExpressJS application robust and easy to maintain