Environment Setup

Create a new project folder and install the following libraries:

  • express
  • body-parser
  • knex
  • bluebird
  • sqlite3
  • eslint (devDependency)
  • eslint-config-airbnb (devDependency)
  • eslint-plugin-import (devDependency)
  • eslint-plugin-jsx-a11y (devDependency)

Create the following folder structure:

/projectFolder
  ./config
  ./controllers
  ./rest
  ./router
  ./data
  ./models
  ./repositories
  ./services
  ./utils

Linter comes first. Setup your favorite one, or use eslint. Create the file .eslintrc.json and put this inside:

{ "extends": "airbnb" }

If you are using Visual Studio Code, it will likely start linting by itself. If not, or if you are not using VSC, get the appropriate plugins for your editor, or automate it from command line. How to do this goes beyond the scope of this guide.

The config folder will predictably host our configuration for various environments our code may run in. Create the following files in it:

// /config/index.js

const _ = require('lodash');
const dev = require('./dev');
const test = require('./test');
const staging = require('./staging');
const production = require('./production');

const environments = { dev, test, staging, production };
const defaultSettings = {
  infrastructure: {
    port: 8888,
  },
  db: {},
};

module.exports = () =>
  (Object.keys(environments)
    .indexOf(process.env.NODE_ENV) > -1
      ? _.merge({}, defaultSettings, environments[process.env.NODE_ENV])
      : _.merge({}, defaultSettings, dev));

This is the main config file, which takes care of setting up our environment configuration variables.

As you probably figured by looking at the require statements, we will create a bunch of environment-specific files, but before we go there, let me explain what this main config file exports.

First, it checks if the environment specified in our environment variable NODE_ENV actually has a corresponding config file.

If so, the _.merge function creates a new object, which it fills with properties from defaultSettings, and then it overwrites the properties of the new object with properties with the same name from the environment-specific config, keeping those without conflicts from defaultSettings.

If the corresponding config file does not exist, it does the same as above, but it defaults to the dev environment config.

It then returns this config object.

So, let us create the other configs.

// /config/dev.js

module.exports = {
  db: {
    client: 'sqlite3',
    useNullAsDefault: true,
    connection: {
      filename: './data/mydb.sqlite',
    },
    migrations: {
      directory: './data/migrations',
    },
  },
};

Have the others (test, staging, production) simply return an empty object. We will not need them for the purpose of this tutorial, but know that they are where you would put in production database connection info, settings relevant for the testing environment etc.

Now we have to prepare the main entry point — i.e. the root index.js file.

// /index.js

const config = require('./config')();
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const router = express.Router();

router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());
router.use('/', (req, res) => res.json('Hello World!'));
app.use(router);

console.log(`Server listening on port ${config.infrastructure.port} in environment ${process.env.NODE_ENV} with config\n${JSON.stringify(config, 0, 2)}`);

app.listen(config.infrastructure.port);

There really is nothing to explain which is not covered by any express tutorial, therefore just try running the file. You should see something like

Server listening on port 8888 in environment dev with config
{
"infrastructure": {
"port": 8888
},
"db": {
"client": "sqlite3",
"useNullAsDefault": true,
"connection": {
"filename": "./mydb.sqlite"
},
"migrations": {
"directory": "./data/migrations"
}
}
}

Setup .gitignore, push everything to your preferred git hosting provider, and have a beverage of your choosing. For when you get back, we continue with our API Layer, i.e. controllers.

results matching ""

    No results matching ""