Category: NodeJs

NodeJs development with Docker (Webpack + ES6 + Babel)

 

In this article we will look at how to use Docker for NodeJs application development and deployment. Here i will be showing you how to use docker-compose utility to bundle NodeJs application as a docker image and run it in a docker container.  For the demonstration purpose, i am going to reuse a NodeJs application that was developed in some other article. I will take you through the step by step process for integrating Docker features and functionalities for  the application that we have developed.

 

we will be using the application developed in following article.

Click here to go the previous article

If you haven’t read the previous article, it is highly recommended to read it before moving forward with this article.

 

Lets remind and brush up our knowledge on the technologies used in the previous article as follows.

  • Express.js :- The application has been developed using Express.js framework.
  • ES6+ :- The source code complies with the ES6+ (ES6 and higher) JavaScript.
  • Babel and babel-loader :- has been used for transpiling the ES6+ source code into ES5 style code. babel-loader has been used with webpack for compiling/transpiling purpose.
  • webpack :- This has been used as the static resource bundling tool (here specially javascript) and executing babel traspiler with babel-loader.

 

Get the source code of the project related to the previous article from GitHub. You can get the source code with following command.

git clone git@github.com:chathurangat/nodejs-webpack-es6.git

 

Once the source code is cloned, add below two empty files to the root of the project.

  • Dockerfile :- the file name should be “Dockerfile” without any extension (NO extension)
  • docker-compose.yml 

Don’t worry about the purposes of these two files for the moment right now. we will discuss the purpose of each file when they contents are added for them.

 

After adding above two files, open the project with your IDE and the project structure should looks like below.

Screen Shot 2018-02-25 at 12.03.13 AM.png

 

NodeJs Application with Express.Js , Babel and Webpack

Since i have demonstrated how to develop NodeJs application with Express.js with ES6+ JavaScript syntaxes and  how to use Babel and Webpack for transpiling and bundling purposes in the previous article, i am not going to repeat the same content here. If you need any clarification regarding this, please refer the previous article.  I will be moving forward with adding Docker for the previous developed application.

 

Moving forward with Docker

Now it is the time to modify the content of the Dockerfile and docker-compose.yml file. Lets look at the purpose of each file in detailed as follows.

Docker is all about creating images from source code and running them in standalone environment called containers.  if you are new to docker and need to get a basic idea, then click here to visit my article about Docker.

 

Dockerfile

Dockerfile contains the instructions and related commands for building the docker image from the project source code.  add the following content for the empty Dockerfile that you have created.

FROM node:alpine
WORKDIR /app
COPY . /app
RUN npm install
ENTRYPOINT ["npm","run","execute"]

 

FROM : This defines the base image for the image that we are building.(The image should be built from this base image). All we said is that, for this image to run, we need the node:alpine image.

 

WORKDIR : This will create a work directory when building the image. Here it will create the “/app” directory as the work directory.  If you go to the bash mode of the container, you can verify that “/app” directory is created with all copies files.

WORKDIR sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that available in the docker file.

 

COPY : command used to copy the given files from local development environment to the docker image. Here the local current working directory (all files in the current working directory) will be copied to “/app” directory.

 

RUN :  RUN command can be used to execute the shell commands when the docker image is being built.

 

ENTRYPOINT : This command will run when the container is created and up. Normally this should contain the command that should be executed in the shell to run the application in the docker container. The command should be given in JSON array format.

According to the above Dockerfile, the command will be:

 npm run execute

 

Here the “execute” is a custom built command and if you observe the scripts of the package.json, then you will find the related command.

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "execute": "webpack && node build/app.bundle.js"
},

 

If you want to learn more about Dockerfile, please click here to visit official documentation about it. 

 

 

What is docker-compose? 

Compose is a tool for defining and running multi-container Docker applications. With Compose, you need to create a YAML file (docker-compose.yml) to configure your application’s services. Then, with a single command, you can create and start all the services from your configuration.

Lets add the contents for the created docker-compose.yml file.

 

docker-compose.yml 

version: '3'

services:
  nodejs-webpack-es6-app:
    image: nodejs-webpack-es6-image
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - 4000:2000

 

According to the above document, docker-compose version is 3. Therefore this document should contain the syntaxes that comply with version 3.

We can declare the list of services under the services. here i have declared only one service which it is built with the source code of this project.  each declared service will be deployed and run in a sperate docker container.

The name of the service is “nodejs-webpack-es6-app“. The service should be deployed with docker image “nodejs-webpack-es6-image“. If the docker image is not available, then build the docker image with using the Dockerfile available in the current working directory.

The service will be running in the container port 2000 and expose it though docker host port 4000. Therefore the service can be accessed externally with:

ip address of the docker host + 4000 (port)

 

 

docker-compose for building and running the application

 

In command shell, go to the directory where the docker-compose.yml file is located and run the below command to run the application.

 

docker-compose  up

 

After running the above command, you can access the application as follows.

 

Testing the Application

Now lets access each HTTP route with postman as follows.

 

GET   http://localhost:4000/

Screen Shot 2018-02-21 at 7.30.27 PM.png

 

GET   http://localhost:4000/products/12

Screen Shot 2018-02-21 at 7.31.26 PM.png

 

POST    http://localhost:4000/products

Screen Shot 2018-02-21 at 7.32.30 PM.png

 

Rebuild the image on source file changing

if you have modified the source code of the application, then you need to remove the old image and rebuild the new image. This can be done with following single command.

 

docker-compose build

 

The source code of this article can be found in GitHub. Click here to get the source code.

 

 

 

NodeJS Simple Application with ECMAScript6 (Webpack + Babel + ES6)

 

In this article, i am going to develop simple NodeJs application that uses following technologies.

  • ES6 (ECMAScript6) based JavaScript Syntaxes
  • Babel for transpiling/compiling ES6 syntaxes into ES5.
  • Webpack for executing babel transpiler and bundling JavaScript  files into a single file
  • ExpressJs as the web application framework for NodeJs

Lets move forward to build our NodeJs application with ExpressJS using ES6+ JavaScript syntaxes.

 

Initializing the project and creating the package.json file.

NodeJs project should be a NPM based (managed) project. That means the dependent packages of the project should be managed with NPM. This can be achieved by creating the package.json file for the project.

The following command can be used to  create the package.json file with required intial configurations.

npm init

Once the npm init command is executed, you need to provide the project related details for the package.json. Please refer the below screenshot.

 

Screen Shot 2018-02-20 at 12.52.40 PM.png

 

Now the package.json is created and we can start the development of our NodeJs application.

 

Creating the project directory structure

The final project structure should looks like below.  Don’t worry at the moment. we will add each of files while we are moving forward with the article.

Screen Shot 2018-02-21 at 5.23.01 PM.png

 

Adding the external libraries and dependencies for the project.

As i have already pointed out, our NodeJs application depends on few external dependencies and  they can be listed as below.

  • Express.js
  • Babel
  • Webpack

We will move forward on installing each of these dependencies.

 

Installing Express.js dependency

Express.js is a web application framework for NodeJs. It has set of utility methods that simplifies the development of  the NodeJs applications.  Express.js is not bundled with NodeJs installation and we need to install it separately. It can be done with following command.

npm install express --save

After executing above command, you can see that ExpressJs  has been added to the dependencies list of the package.json file.

 

Installing Babel dependencies

Babel is used to transpile ES6+ based source code into ES5. This should be installed as a development related dependency as the transpiling is happened in the development environment. The following command will install the babel related dependencies in the development environment.

npm install --save-dev babel-core babel-loader babel-preset-env babel-polyfill
  • babel-core :- includes the babel core modules
  • babel-loader  :-  loader for running the ES6+ and JSX with webpack. This will be configured in the webpack.config.js
  • babel-preset-env :- (or any other preferred preset). The preset will also be configured in the webpack.config.js or can be defined in .babelrc file
  • babel-polyfill :- A polyfill is a piece of code, that replicate the fully ES6+ environment into the targeted runtime environment. Therefore some of the transpiled ES6+ based methods and objects can be executed in ES5 environment without any runtime error.  If you want to learn more about babel-polyfill, please click here to go to my article on that.

If you want to learn more about babel or webpack, please click here to visit article i have written on babel with Webpack.

Once the above command is executed successfully, you can see that the babel dependencies are installed under the devDependencies of the package.json

 

Installing the webpack dependencies

So we will be moving forward with setting up webpack. First we need to install the webpack dependencies for the project and there are two dependencies required.

  • webpack
  • webpack-dev-server

These dependencies can be installed with following command.

npm install webpack webpack-dev-server --save-dev

 

So once the dependencies are installed, we need to create a webpack.config.js file and do the necessary configuration for the webpack.

 

–save  : This will create a dependency entry under the dependencies of the package.json

–save-dev  : This will create a dependency entry under the devDependencies of the package.json

 

Once all the required dependencies are installed, the package.json of the project should looks like below. (This is just a sample snapshot of package.json and the versions of the dependencies can be different)

"dependencies": {
  "babel-polyfill": "^6.26.0",
  "express": "^4.16.2"
},
"devDependencies": {
  "babel-core": "^6.26.0",
  "babel-loader": "^7.1.2",
  "babel-preset-env": "^1.6.1",
  "webpack": "^3.11.0",
  "webpack-dev-server": "^2.11.1"
}

 

Lets look at each source file in detailed.

 

index.js 

create an index.js file in the root of your project.  The file should contains the following source. 


import express from "express";
const app = express();
import routes from "./src/routes";
let port = 2000;
app.listen(port, ()=> {
console.log(" server is started and listen on port [" + port + "]");
});
//all the requests will be handled by routes middleware
app.use("/", routes);

view raw

index.js

hosted with ❤ by GitHub

This is the entry point of your NodeJs application. This contains the application logic for spinning up the web server and handling the application routes (HTTP Routes)

This will create a web server and listen for the port 2000.  All the requests comes to the web server will be handled by the routes module developed in this application. we go through the routes module of the application in a while.

As you can see that we are using the ES6 based import here instead of ES5 based require. If you observe most of the source code in this project, you will notice that we have used ES6+ based syntaxes most of the places.

 

RequestHandlerService.js

This class hans been added demonstrate the robust features of ES6+ (Just to show the beauty of ES6+). This class will contain a simple method of handling HTTP request.


export class RequestHandlerService
{
static async handleHttpRequest(requestMethod, urlPattern)
{
return "HTTP " + requestMethod + " " + urlPattern + " received";
}
}

This method has been annotated with async. Therefore it guarantees that the method will return a promise.

 

routes.js

This file will contains the all the HTTP Route handlers related to this project. I am not going to explain all the route handlers here. I will just describe few of them for your understanding.


import {RequestHandlerService} from "./service/RequestHandlerService";
const express = require('express');
const router = express.Router();
router.all('/*', function (request, response, next) {
console.log(" this will be applied to all routes ");
next();
});
router.get("/", async (request, response)=> {
let message = await RequestHandlerService.handleHttpRequest(request.method, request.path);
response.status(200).json({
"message": message
});
});
router.get("/products", (request, response)=> {
response.status(200).json({
"message": "HTTP " + request.method + " Request with URL Pattern " + request.path
});
});
router.get("/products/:id", (request, response)=> {
response.status(200).json({
"message": "HTTP " + request.method + " Request with URL Pattern " + request.path
});
});
router.post("/products", (request, response)=> {
response.status(200).json({
"message": "HTTP " + request.method + " Request with URL Pattern " + request.path
});
});
module.exports = router;

view raw

routes.js

hosted with ❤ by GitHub

 

Lets look at some of the selected routes in detailed.  If you want to learn more about ExpressJS routing, it is recommended to read http://expressjs.com/en/guide/routing.html

router.all('/*', function (request, response, next) {
    console.log(" this will be applied to all routes ");
    next();
});

 

There is a special routing method, app.all(), used to load middleware functions at a path for all HTTP request methods. For example, the above handler is executed for requests to the route “/*” (for any route) with HTTP GET, POST, PUT, DELETE, or any other HTTP request method supported.

next() will delegate the request to the next available middleware or route.

 

router.get("/", async (request, response)=> {
    
    let message = await RequestHandlerService.handleHttpRequest(request.method, request.path);

    response.status(200).json({
        "message": message
    });
});

 

handleHttpRequest (Async method)  of the RequestHandlerService has been invoked in this route handler. Since the route handler function(arrow function) is modified with async keyword,it is possible to use the await keyword to wait for the result from the method invocation.

If you observe the last line of the file, you can see that we have export the module router. So the outside script can use this module.

module.exports = router;

 

webpack.config.js

Add the webpack.config.js file in the root of your project.The webpack.config.js related to this project can be shown as follows.


const path = require('path');
module.exports = {
target: 'node',
entry: {
app: [
'babel-polyfill',
'./index.js'
]
},
output: {
path: path.resolve(__dirname, 'build'),
filename: 'app.bundle.js'
},
module: {
loaders: [{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['env']
}
}]
}
};

Using webpack, running/traspiling your JavaScript and JSX through Babel is a simple as adding the babel-loader to your webpack.config.js. It has been added as a loader and it will go through all js and JSX files. It uses the babel-preset-env as defined and excludes the files in the node_modules directory.

babel-polyfill will be added and managed by the webpack. In addition, index.js has been designated as a entry point js file for the project.

The output file will be stored in the build directory using file app.bundles.js.

 

Testing and Running the application.

Now we have come to the final section of our article. Here we will run the nodeJs application and test each application route.  In oder to run the application , we need to transpile the ES6+ syntaxes of the project into the ES5.

 

Transpiling/Compiling ES6+ syntaxes into ES5 with Babel and Webpack

Now we need to transpile/convert the ES6+ based source code into ES5 based source. This can be done with babel. In this project we use webpack to execute babel using babel-loader for the source code transpiling.  In oder to achieve this, go to the root of the project where your webpack.config.js is located.  Then run the following command to build the project.

webpack

 

Running the NodeJs application

Then you can see that project is build and final file is created in build/app.bundle.js. This is the file that should be run with node.

node build/app.bundle.js

 

Once the above command is executed, it will create a web server and up the NodeJs project. please refer the below screenshot.

Screen Shot 2018-02-21 at 5.01.31 PM.png

You can see that server is up and application listen on port 2000 for incoming  HTTP requests.

 

Executing both transpiling and running commands with NPM

We can combine both commands together and add a new command under the scripts of the package.json

"execute": "webpack && node build/app.bundle.js"

 

Now you can run following command to build and run the project. It will first build the project with webpack and then run the build file with node.

npm run execute

 

 

Now lets access each HTTP route with postman as follows. 

 

GET   http://localhost:2000/

Screen Shot 2018-02-21 at 7.30.27 PM.png

 

GET   http://localhost:2000/products/12

Screen Shot 2018-02-21 at 7.31.26 PM.png

 

POST    http://localhost:2000/products

Screen Shot 2018-02-21 at 7.32.30 PM.png

 

The source code of this article can be found in GitHub. Click here to get the source code. 

 

How to set up the project clone from GitHub

The source code has been added without the node_modules directory. Therefore run the following command to install the required dependencies for the project. This will install the dependencies locally for your project.

npm install

 

After that you can run project with following command.

npm run execute

 

This is the end of the article. If you have any concern or need any help, feel free to contact me.