DEV Community

loading...
Cover image for Creating a Microservices App with Dockerized Express Gateway.

Creating a Microservices App with Dockerized Express Gateway.

Naseef M Abdus Sattar
I am a Backend Software Engineer, experienced in Node JS with more than 3 years of industrial expertise under my belt. My new learning enthusiasm is in Data science.
Updated on ・7 min read

In the time of creating software products that need to be highly scalable, microservices app looks like the most simple solution to them all. During my journey as a software developer and working in a project which requires the software to accommodate nearly a million users on demand, I have seen and felt that microservices solve the ultimate problem of proper utilisation of resources and increase the scalability of different services. This removes coupling between different services and help them run independently.

What is an API Gateway?

One of the most fundamental requirement of creating a microservices is the API gateway. An API gateway is an infrastructure layer that sit in front of the microservices. Its purpose is to serve requests from the client by routing it to the right microservice.

api-gw-role

This article is about how to create a very simple Node.js microservices application with the help of Dockerized Express API Gateway

Pre-Requisite

This sample project requires few things.

  • Docker (V19.03.2 or above) installed in your machine.
  • Node v10 or above.
  • Basic knowledge of Docker commands.
  • Ngrok or any other free tunnelling services for exposing localhost to the web

I am doing this project in Mac OSX 11.4.

Getting the Docker Image of Express Gateway and running it.

Run this below command in your terminal to pull the docker image.

docker pull express-gateway

After pulling the image, create a folder and name it Config. Open terminal in that folder and run the below commands.

touch gateway.config.yml
touch system.config.yml

Firstly copy and paste the below YAML code in gateway.config.yml. (I used Sublime Text editor to do this, but any other text editor is fine)

http:
  port: 8080
admin:
  port: 9876
  host: localhost
apiEndpoints:
  api:
    host: localhost
    paths: '/ip'
serviceEndpoints:
  httpbin:
   url: 'https://httpbin.org'
policies:
  - basic-auth
  - cors
  - expression
  - key-auth
  - log
  - oauth2
  - proxy
  - rate-limit
pipelines:
  default:
    apiEndpoints:
      - api
    policies:
    # Uncomment `key-auth:` when instructed to in the Getting Started guide.
    # - key-auth:
      - proxy:
          - action:
              serviceEndpoint: httpbin 
              changeOrigin: true
Enter fullscreen mode Exit fullscreen mode

Copy and paste the YAML code below in system.config.yml file.

# Core
db:
  redis:
    emulate: true
    namespace: EG

#plugins:
  # express-gateway-plugin-example:
  #   param1: 'param from system.config' 

crypto:
  cipherKey: sensitiveKey
  algorithm: aes256
  saltRounds: 10

# OAuth2 Settings
session:
  secret: keyboard cat
  resave: false
  saveUninitialized: false
accessTokens:
  timeToExpiry: 7200000
refreshTokens:
  timeToExpiry: 7200000
authorizationCodes:
  timeToExpiry: 300000

Enter fullscreen mode Exit fullscreen mode

Now create another folder, named models, in Config. Open terminal at models and run the below commands to create three files.

touch applications.json
touch credentials.json
touch users.json

Copy and paste the below code in applications.json.

{
  "$id": "http://express-gateway.io/models/applications.json",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "redirectUri": {
      "type": "string",
      "format": "uri"
    }
  },
  "required": [
    "name"
  ]
}
Enter fullscreen mode Exit fullscreen mode

After that copy and paste the below code in credentials.json

{
  "$id": "http://express-gateway.io/models/credentials.json",
  "type": "object",
  "definitions": {
    "credentialBase": {
      "type": "object",
      "properties": {
        "autoGeneratePassword": {
          "type": "boolean",
          "default": true
        },
        "scopes": {
          "type": [
            "string",
            "array"
          ],
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "autoGeneratePassword"
      ]
    }
  },
  "properties": {
    "basic-auth": {
      "allOf": [
        {
          "$ref": "#/definitions/credentialBase"
        },
        {
          "type": "object",
          "properties": {
            "passwordKey": {
              "type": "string",
              "default": "password"
            }
          },
          "required": [
            "passwordKey"
          ]
        }
      ]
    },
    "key-auth": {
      "type": "object",
      "properties": {
        "scopes": {
          "type": [
            "string",
            "array"
          ],
          "items": {
            "type": "string"
          }
        }
      }
    },
    "jwt": {
      "type": "object",
      "properties": {}
    },
    "oauth2": {
      "allOf": [
        {
          "$ref": "#/definitions/credentialBase"
        },
        {
          "type": "object",
          "properties": {
            "passwordKey": {
              "type": "string",
              "default": "secret"
            }
          },
          "required": [
            "passwordKey"
          ]
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Lastly copy and paste the below code for users.json

{
  "$id": "http://express-gateway.io/models/users.json",
  "type": "object",
  "properties": {
    "firstname": {
      "type": "string"
    },
    "lastname": {
      "type": "string"
    },
    "username": {
      "type": "string"
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "redirectUri": {
      "type": "string",
      "format": "uri"
    }
  },
  "required": [
    "username",
    "firstname",
    "lastname"
  ]
}
Enter fullscreen mode Exit fullscreen mode

For this article, we will only focus on configuring the gateway.config.yml file. I will walk through that after setting up and running the docker.

Now that our Config folder is all ready, run the below Docker command to spin up a Docker container.

docker run -d --name express-gateway \
-v /Users/naseef/Config:/var/lib/eg \
-p 8080:8080 \
-p 9876:9876 \
express-gateway

In order for our Express Gateway Docker container to work properly we need to mount a volume with configuration files and volumes. The Config folder will be mounted and it contains all the configuration files and volumes inside it. Please replace '/Users/naseef/Config' in the above Docker to your own path of Config folder.

This should start and run the Docker container named express-gateway. To make sure it is running, run docker ps in the terminal and check.

Creating two simple different microservices with two endpoints

We are going to create two GET Endpoints to act as our microservices.

Create a folder in any directory of your choice named Actor. Setup an Express server in this folder. I used npm init command to setup my package.json, but you can choose other ways to setup the Express server.

Upon creating the package.json, run npm install express --save in Actor to install Express.

Create a file named actor.js and paste the code below.

let express = require('express');
let app = express();
app.listen(3000, () => {
 console.log("Server running on port 3000");
});

app.get("/actors", (req, res, next) => {
    let array_actors = [
        "Tom Cruise",
        "Johnny Depp",
        "Di Caprio",
        "Russel Crowe",
        "Tom Hanks"
    ];
    res.json(array_actors);
});

Enter fullscreen mode Exit fullscreen mode

Run node actor.js to start this server on port 3000 of localhost. Hit the browser (or use Postman) with this url.

http://localhost:3000/actors.

The result should be like this.

[
    "Tom Cruise",
    "Johnny Depp",
    "Di Caprio",
    "Russel Crowe",
    "Tom Hanks"
]
Enter fullscreen mode Exit fullscreen mode

In the same way as above create Movie folder, setup an Express server and create a file named movie.js. Add the below code in the movie.js file

let express = require('express');
let app = express();
app.listen(8000, () => {
 console.log("Server running on port 8000");
});
app.get("/movies", (req, res, next) => {
    let array_movies = [
        "Mission Impossible",
        "Pirates of Carribean",
        "Inception",
        "Gladiator",
        "The Terminal"
    ];
    res.json(array_movies);
});

Enter fullscreen mode Exit fullscreen mode

Run node movie.js to start this server on port 8000 of localhost. Hit the browser (or use Postman) with this url.

http://localhost:8000/movies.

The output should be something like this.

[
    "Mission Impossible",
    "Pirates of Carribean",
    "Inception",
    "Gladiator",
    "The Terminal"
]
Enter fullscreen mode Exit fullscreen mode

Ngrok to expose the GET Endpoints in the web.

Use Ngrok to expose port 3000 and 8000 in the web. If you have trouble running two Ngrok sessions at the same time, follow this link.

I will leave the details of exposing the API in the web through Ngrok out of the scope of this tutorial.

Upon exposing the endpoints with Ngrok, we have the below endpoints for the services.

Movie services: http://412143bfb37a.ngrok.io/movies
Actor services: http://eed882f0ffe3.ngrok.io/actors

Now that we have two running microservices, it is time to configure our gateway.config.yml file to route both these services through our Dockerized Gateway.

Configure gateway.config.yml file of Express Gateway

The Express Gateway accepts requests from the clients and directs them to the microservice in charge of the particular request. For example, if a request is made through the gateway, http://localhost:8080/actors, the request is directed to the actor microservice and a request such as http://localhost:8080/movies is directed to the movie microservice. In summary, each request is accepted through a server and directed to their various host. Let’s set up our Docker to implement this.

Expose the endpoints to the gateway

  • Navigate into Config folder
  • Open gateway.config.yml file.

In the apiEndpoints section, we create an API endpoint named “actors”, define the host and path. The path defined is an array of paths we would like to match. This is to cover for all URLs that match the path pattern. Repeat the same for the movies endpoint. For further details about matching patterns for hostname and paths, check out the Express Gateway endpoint documentation.

With these, the Express gateway can accept external APIs(request from the client) of these formats ‘http://localhost:8080/actors’ or ‘http://localhost:8080/actors/*’

Screenshot 2021-07-03 at 11.37.56 PM

Create the Service Endpoints to the gateway

The service endpoints are the endpoints of our microservices, in this case, the actor and the movie microservices. The external request from the API endpoints is directed to the service endpoints. In the serviceEndpoints section still in the same gateway.config.yml file, we create services and define their URLs. For actors, we call its service endpoints actorService and add its endpoints ‘http://eed882f0ffe3.ngrok.io’. Repeat the same for the movie service endpoint.

Screenshot 2021-07-03 at 11.45.18 PM

Finishing up this long post!

Now let’s tie up the API endpoints and the service endpoints. It is configured in the Pipelines section. This is where we connect the API endpoints and the service endpoints.

You will find that a default has been pre-defined, so we need to just define ours. You can copy and paste the default pipeline and then change some of the details. We name our pipeline “actorPipeline”, add the endpoint name which is “actors”. Find the serviceEndpoint in the proxy policy of the actorPipeline pipeline and add our service endpoint name which is “actorService”. Repeat the procedure for the movie pipeline.

http:
  port: 8080
admin:
  port: 9876
  host: localhost
apiEndpoints:
  api:
    host: localhost
    paths: '/ip'
  actors:
    host: localhost
    paths: ['/actors','/actors/*']
  movies:
    host: localhost
    paths: ['/movies','/movies/*'] 

serviceEndpoints:
  httpbin:
   url: 'https://httpbin.org'
  actorService:
   url: 'http://eed882f0ffe3.ngrok.io'
  movieService:
   url: 'http://412143bfb37a.ngrok.io'   
policies:
  - basic-auth
  - cors
  - expression
  - key-auth
  - log
  - oauth2
  - proxy
  - rate-limit
pipelines:
  actorPipeline:
    apiEndpoints:
      - actors
    policies:
    # Uncomment `key-auth:` when instructed to in the Getting Started guide.
    # - key-auth:
      - proxy:
          - action:
              serviceEndpoint: actorService 
              changeOrigin: true
  moviePipeline:
    apiEndpoints:
      - movies
    policies:
    # Uncomment `key-auth:` when instructed to in the Getting Started guide.
    # - key-auth:
      - proxy:
          - action:
              serviceEndpoint: movieService 
              changeOrigin: true
  default:
    apiEndpoints:
      - api
    policies:
    # Uncomment `key-auth:` when instructed to in the Getting Started guide.
    # - key-auth:
      - proxy:
          - action:
              serviceEndpoint: httpbin 
              changeOrigin: true
Enter fullscreen mode Exit fullscreen mode

Now our whole system is ready. Save the gateway.config.yml file and restart the Docker container.

docker restart express-gateway

The Ngrok and Node services should be running. If all are functioning properly we should be getting proper output by using our URLS

http://localhost:8080/actors
http://localhost:8080/movies

This is the end of this Microservice App creation with Dockerized Express-gateway. If you face any errors or any difficulties feel free to comment below.

Thank you. Happy Coding

Discussion (0)