Create a new ES6 project

Create a new NodeJS project with battery included.

Avatar of Author
Jean-Christophe BaeySeptember 17, 2019
NodeJS 10+ LTS
Photo by Samuel Zeller on Unsplash

NodeJS 10+ supports most of ES6 specifications but missing module import. Likewise, setting up a good linting configuration (Airbnb) will prevent you to write dirty code :-)

Prerequisites

Create a new project

  • Initialization
$ yarn init -y
$ mkdir src
  • Update package.json to set the project as private and add the metadata
{
  "description": "My new project",
  "private": true,
  "author": {
    "name": "Jean-Christophe Baey",
    "email": "jcbaey@gmail.com"
  },
}
  • Add dev tooling (nodemon, babel, jest and eslint).
$ yarn add nodemon @babel/core @babel/cli @babel/node @babel/preset-env prettier eslint eslint-plugin-node jest jest-cli eslint-plugin-jest eslint-config-prettier eslint-plugin-prettier --dev

$ yarn add dotenv

$ yarn add eslint-config-airbnb-base --dev
$ npx install-peerdeps --dev eslint-config-airbnb-base --yarn

Tools details:

  • nodemon: watch your source code and auto-restart your application on changes
  • babel: transpile your source code into a node 10+ source code
  • jest: test framework
  • eslint: code linter
  • prettier: code formatter
  • dotenv: ease the management of environment variables
  • eslint-config-airbnb-base: code linter rules from Airbnb
  • eslint-config-prettier: Make eslint rules compatible with prettier rules to avoid conflicts.
  • eslint-plugin-prettier: enable eslint to run prettier from command line (otherwise only VSCode will run prettier)
  • Update package.json and add:
 "scripts": {
    "build": "babel src -d dist --source-maps",
    "clean": "rm -rf dist",
    "debug": "nodemon --exec babel-node --inspect-brk src/index.js ",
    "serve": "node dist/index.js",
    "start": "nodemon src/index.js --exec babel-node",
    "test": "jest --runInBand --verbose",
    "coverage": "jest --coverage --runInBand --verbose",
    "eslint": "eslint src/**/*.js --ignore-pattern \"node_modules/\"",
    "lint": "eslint src --fix",
    "release": "npm version patch && git push && git push --tags"
  },
  "babel": {
    "presets": [
      [
        "@babel/preset-env",
        {
          "targets": {
            "node": "current"
          }
        }
      ]
    ]
  },
  "engines": {
    "node": ">=10"
  },
  "eslintConfig": {
    "plugins": [
      "node",
      "jest"
    ],
    "extends": [
      "airbnb-base",
      "plugin:prettier/recommended",
      "plugin:jest/recommended",
      "prettier"
    ],
    "rules": {
    },
    "env": {
      "jest/globals": true,
      "node": true,
      "es6": true
    },
    "parserOptions": {
      "sourceType": "module",
      "ecmaVersion": 2018
    }
  },
  "prettier": {
    "singleQuote": true,
    "trailingComma": "all",
    "arrowParens": "always",
    "printWidth": 100
  }

Create your first source file

  • Create index.js in ./src :
import { name, version } from '../package.json';
require('dotenv').config();

console.log(`Starting ${name} v${version} in ${process.env.NODE_ENV} mode`);

Initilize GIT

  • Add a .gitignore file at the root of the project (source: gitlab)
node_modules/
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pids
*.pid
*.seed
*.pid.lock
lib-cov
coverage
.nyc_output
.lock-wscript
build/
dist/
jspm_packages/
typings/
.npm
.eslintcache
.node_repl_history
*.tgz
.yarn-integrity
.env
.DS_Store
.tmp
.idea
  • Initialize the repository
$ git init

Debug configuration

Add a .vscode directory with launch.json :

{
  // Use IntelliSense to learn about possible Node.js debug attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
      {
          "type": "node",
          "request": "launch",
          "name": "Launch Program",
          "program": "${workspaceRoot}/src/index.js",
          "sourceMaps": true,
          "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/babel-node"
      },
      {
          "type": "node",
          "request": "attach",
          "name": "Attach for debug",
          "restart": false,
          "protocol": "inspector",
          "sourceMaps": true
      }
  ]
}

Create you docker image (optional)

  • Create a .dockerignore files at the root of your project
node_modules
importedFiles
logs
yarn-error.log
.vscode
.git
  • Create a Dockerfile file
FROM node:10.15-alpine

LABEL maintainer="your name"

# Create app directory
RUN mkdir -p /usr/app
WORKDIR /usr/app

# Create a dedicated user
RUN addgroup -S nodejs && adduser -S -g nodejs nodejs

# Bundle app source
COPY package.json .
COPY yarn.lock .

# unknown is the default, but you can override it with --build-arg APP_VERSION=0.0.1 during docker build
ARG APP_VERSION=unknown
ENV APP_VERSION $APP_VERSION

# production is the default, but you can override it with --build-arg NODE_ENV=development during docker build
ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV

# unknown is the default, but you can override it with --build-arg RELEASE_DATE=$(date +"%Y/%m/%d") during docker build
ARG RELEASE_DATE=unknown
LABEL com.your-app.author="your name" \
      com.your-app.release-date=$RELEASE_DATE \
      com.your-app.release-version=$APP_VERSION


RUN yarn install
RUN yarn dist

# Remove dev packages
RUN npm prune --production

# Use node user
USER nodejs

CMD [ "yarn", "serve" ]
  • Build your docker image
#!/bin/bash

APP_VERSION=$(node -pe "require('./package.json').version")
PACKAGE_NAME=$(node -pe "require('./package.json').name")
RELEASE_DATE=$(date +"%Y/%m/%d")
DOCKER_REGISTRY=

docker build -t ${DOCKER_REGISTRY}/${PACKAGE_NAME}:${APP_VERSION} \
--build-arg APP_VERSION=${APP_VERSION} \
--build-arg RELEASE_DATE=${RELEASE_DATE} \
.

docker tag ${DOCKER_REGISTRY}/${PACKAGE_NAME}:${APP_VERSION} ${DOCKER_REGISTRY}/${PACKAGE_NAME}:latest

docker push ${DOCKER_REGISTRY}/${PACKAGE_NAME}:${APP_VERSION}
docker push ${DOCKER_REGISTRY}/${PACKAGE_NAME}:latest

Run your app in production

Using Docker

  • run your image in Kubernetes

Without Docker

  • use PM2 to keep your app always on and manage the logs. It can also provide you load balancing & zero second downtime reload thanks to the cluster mode.

👉 Don't forget to follow me on Twitter to be notified when new posts are available!