Express Project Best Practice

In the previous section we saw the Express CLI tool used to create Express projects

express --view=pug myapp

This way of creating Express projects is best practice. It gives us a uniform project structure across projects making each project easy to navigate and maintain, thus resulting in fewer errors. Things out customers and users appreciate.

For personal as developers there are a couple of more steps involved in the creation of a good development environment fort each new project. Let us call it the Recipe. It goes like this:

Example 20.5. The Recipe for Best Practice
express --view=pug <projectname>
cd <projectname>
npm install
git init
echo 'node_modules/' > .gitignore
git add .
git commit -m 'initial commit, Express installed, ready for own code'

These commands may be put into a script in your command line shell, if you wish.

When this has been done you have a work setup with skeleton project files, git initialized and initially committed. Thsi may be pushed into an empty repository created on Gitlab, Bitbucket, or Github, or similar services. You are close to being ready to code. One or two improvements for practical reasons.

The Express CLI wrote to you:

   run the app:
     $ DEBUG=demoproject:* npm start

Let us improve your package.json slightly, so that it contains:

{
  "name": "demoproject",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "pretest": "npm install",
    "test": "DEBUG=demoproject:* NODE_ENV=development node ./bin/www",
    "start": "DEBUG=demoproject:* NODE_ENV=production node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "pug": "2.0.0-beta11"
  }
}

The lines 6, 7, and 8 have replaces what was in the scripts object before. Henceforth we may test the project with:

npm test

or we may run production with:

npm start

from the CLI.


Let us try

npm test

> demoproject@0.0.0 pretest /home/nml/nodeProjects/nodejsexps/demoproject
> npm install

audited 54 packages in 0.663s
found 0 vulnerabilities


> demoproject@0.0.0 test /home/nml/nodeProjects/nodejsexps/demoproject
> DEBUG=demoproject:* NODE_ENV=development node ./bin/www

  demoproject:server Listening on port 3000 +0ms

Notice the running of npm install. It runs because of the pretest clause in scripts. If we decide to run test, the pretest kicks in just prior to that. Now we may point our browser at the designated port.

Figure 20.1. The resulting Express Site
The resulting Express Site

A Look at the Application

Again, every Express application has an app.js module configuring the application with various external modules for use in the application. This external software is attached to the app object which is the shared.

The user of the app is the bin/www, a JavaScript file that we don't edit directly. With respect to the native Node.js we have worked with so far, app.js, and bin/www are two JavaScript files corresponding what we have called main.js, and bin/server.js before.

We have seen the file structure from the early project above.

Example 20.6. The app.js File
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

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

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

The bin/www is a JavaScript file in spite of the lacking.js filename suffix. It is not particularly interesting in that we do not edit it.

Now let us peep at the routing. There are two files, but for the time being, we are only interested in one of them

Example 20.7. The Main Routing File, routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;