Deploying RESTful APIs using Node.js, Express 4 to Kubernetes clusters

In this tutorial, we will go over how to build RESTful APIs using the Node.js Express framework, test them locally using docker-compose. We will then proceed to deploy this application to the Kubernetes.

1. Introduction

Express is a backend development framework built on top of Node.js, it enables the implementation of the client-server architecture. With its flexibility, it allows for the customization of the API endpoints, consequently, fitting our needs.

2. Prerequisites

To follow along with this tutorial, you need the following:

  • Node.js downloaded and installed in your local development environment.
  • Basic knowledge in Node.js’ Express framework.
  • RESTful APIs design.
  • Basic knowledge in Docker
  • Basic knowledge in kubernetes

3. Objectives

By the end of this article, you should be able to create a complete dynamic Express application and deploy it to the cloud using Docker.

4. Node.js application setup

Let’s start by importing required modules and create a running server:

//this node application is located in the index.js file
const http = require("http");

http.createServer(function (req, res) {
  
   res.writeHead(200, {'Content-Type': 'text/plain'});
 
   res.end('Hello World\n');
   
}).listen(8000);

console.log('Server started at http://127.0.0.1:8000/');

Now execute this application by running the command on the command line:

node index.js

Execution output:

The server started at http://127.0.0.1:8000/

5. Express packages setup

Add the following contents in your server.js script:

const express    = require('express');      
const app        = express();                
const bodyParser = require('body-parser');
// import the student schema defined in the student.js file
const  Student = require('./models/student'); 
//register router middleware
const router = express.Router();  


app.use(bodyParser.urlencoded({ extended: true }));  
app.use(bodyParser.json());

const port = process.env.PORT || 8000;        

const config = require('./config');

const mongoose = require('mongoose');

mongoose.connect(config.db[app.settings.env]); 

In the script above, we imported the Express package. Additionally, we imported packages that will aid in running our Express application and setting up a connection to the database.

Now that we have got a connection to the MongoDB database server, let’s define the model that we will use to get the list of students from a school database.

const mongoose     = require('mongoose');  
const Schema       = mongoose.Schema;

const StudentSchema   = new Schema({  
    student_id: String,
    name: String,
    registration_number: String,
    course: String,
    year_of_study: Number,
},
{
    timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});

module.exports = mongoose.model('Student', StudentSchema);  

In the model above, we set up the student details we will be getting via our API.

6. RESTful APIs implementation

Now that we’ve set up our model and server file, in this section, let’s implement our RESTful APIs and deploy our application to the cloud.

router.get('/students/:student_id', function(request, response) {  
    Student.findOne({student_id: request.params.student_id}, function(err, Student) {

        if (err) 
        {
        response.status(500);

        response.setHeader('Content-Type', 'application/vnd.error+json');
        response.json({ message: "An error occurred, unable to get student details"});

    } 
    else if (Student == null) 
    {

        response.status(404);
        response.setHeader('Content-Type', 'application/vnd.error+json');
        response.json({ message: "ProductQuantity not found for product_id "+request.params.student_id});

    } 
    else 
    {

        response.status(200);
        response.setHeader('Content-Type', 'application/hal+json');

        let student_resource = halson({
        student_id: Student.student_id,
        name: Student.name,
        course: Student.course,
        year: Student.year_of_study,
        registration_number: Student.registration_number,
        created_at: Student.created_at
        }).addLink('self', '/students/'+Student.student_id)
        //response
        response.send(JSON.stringify(student_resource));

    }
    });    
});

// let's now register our routes
app.use('/', router);

// now start the server on port 8000

app.listen(port);  
console.log('Starting server on port ' + port);  

7. Dockerizing the Express application

Now that we’ve defined our core application API logics, let’s proceed to our main aim of the tutorial, dockerizing your RESTful Node.js Express application.

This section assumes you have Docker up and running in your Ubuntu machine.

Let’s proceed and define the contents of the Dockerfile to direct docker on how to build a container image of our Express application.

# the base image from which the app is built upon
FROM node: latest 
# Runs the mkdire command to create /usr/src/app inside docker container
RUN mkdir -p /usr/src/app  
# Sets the work directory to /usr/src/app 
WORKDIR /usr/src/app  
# Copies the contents of the current directory into the working directory inside the # docker container
COPY . /usr/src/app
# Exposes port 8000 outside the docker container
EXPOSE 8000  
# Runs the npm install command to install dependencies
RUN npm install  
# Provides the command required to run the application
CMD ["npm", "start"] 

This Dockerfile uses npm to install modules in our RESTful application. Let’s now proceed and set up the docker-compose configuration file that we’ll use to launch the Node.js Express application (including the MongoDB instance).

-------------------------
# Service name
student:  
# build in the current directory
  build: .
  # command to run the app
  command: npm start
  # Maps port 8000 inside docker container to port 8000 outside docker container
  ports:
  - "8000:8000"
  # linking the student to mongodb container
  links:
  - mongodb
  # env variables
  environment:
    - NODE_ENV=production
    - MONGODB_ADDRESS=mongodb
# mongodb service
mongodb:  
  # pulling mongodb image
  image: mongo

8. Setup YAML service to deploy Dockerized Node.js Express application

Now that we’ve dockerized our application locally, the next step involves deploying the application to the cloud.

Let’s proceed and set up the service to deploy the app as shown below

//service.yaml file
-----------------------------
services:

  inventory:
    git_url: git@github.com:myexample.git
    git_branch: main
    command: npm start
    build_root: .
    ports:
      - container: 8000
        http: 80
        https: 443
    env_vars:
      NODE_ENV: production

databases:  
  - mongodb

Note, make sure you change your git URL in the above service.

You can now log in to your favorite cloud vendor to deploy your dockerized application.

Now that we have a Docker container image, we need to create a deployment file. In the root directory, create a new file called deployment.yaml. This file will deploy the application to the Kubernetes engine.

Add the following snippets to the file:

apiVersion: v1
kind: Service
metadata:
  name: rest-test-service
spec:
  selector:
    app: rest-test-app
  ports:
  - protocol: "TCP"
    port: 3000
    targetPort: 8000
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rest-test-app
spec:
  selector:
    matchLabels:
      app: rest-test-app
  replicas: 5
  template:
    metadata:
      labels:
        app: rest-test-app
    spec:
      containers:
      - name: rest-test-app
        image: rest-test-app
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8000

The file has two parts:

  1. Service – The service acts as the load balancer. A load balancer is used to distribute requests to the various available servers.
  2. Deployment will act as the intended application. The user request hits the load balancer, then the load balancer distributes the request by creating the number of replicas defined in the deployment.yaml file. For example, in our case, we have five replicas for scalability, meaning that we will have 5 instances running at a time.

The benefit of multiple replicas is that if an instance crashes, the other application instances continue running.

The deployment.yaml file is connected to the Docker image created earlier, therefore to deploy the application to the Kubernetes cluster, we use the Docker image. The image will automatically create containers for the application when we deploy the application.

9. Deploying to Kubernetes service

We have dockerized our RESTful application, and now we need to deploy it to a Kubernetes engine.

Execute the command below in your terminal:

kubectl apply -f deployment.yaml

This command will deploy our service and application instances to the Kubernetes engine. After executing this command, we should be able to see that the rest-test-service and the rest-test-app are created successfully.

10. The deployment dashboard

Minikube and Kubernetes provide a dashboard to visualize the deployment. To see our deployment in that dashboard, execute the command below in your terminal.

minikube dashboard

We can see that our rest application was deployed and we can see the number of running instances. If a request is made, the load balancer distributes the number of hits the request had on the instances.

11. Accessing the application

We can access the application using the command below:

minikube start service: rest-test-service

12. Conclusion

In this tutorial, we’ve covered the key concepts of Node.js Express application RESTful APIs. We discussed how we can dockerize this application locally using Docker and deploy it to the Kubernetes.

Happy coding!

Related posts:

Getting Started with Google Drive Node.js API
MySQL with Node.js
How To Develop A Chat Bot With Node.js
Handling Continuous Integration And Delivery With GitHub Actions
How To Build and Test a Node.js REST API with Express on Ubuntu 18.04
Debugging a Node.Js app using Chrome Dev Tools
Implementing Secret Key Cryptography in JavaScript
How To Build A CLI Tool With Node.js And PhantomJS
Uploading Images to Cloudinary using Node.js
How to Build a Custom URL Shortener using Node.js, Express, and MongoDB
Why Node.js is Great for Backend Development?
Web Scraping With Node.js
Understanding Asynchronous Control Flows in Node.js Using Async.js
Build a Ticketing App with Adonis.js and Vue.js
Node.js vs Django
Getting Started with Strapi API CMS
Building A Node.js Express API To Convert Markdown To HTML
JavaScript Particles Effect with tsParticles
Developing A Chatbot Using Microsoft’s Bot Framework, LUIS And Node.js
Creating a Weather app in Node.js using the Openweathermap API
Building your First Telegram Bot using Node.js and Telegraf
Session Management in Node.js using ExpressJS and Express Session
Node.js Callback Concept
Next Generation Server Compression With Brotli
Converting a Static Site to a Static Site Generator
Getting Started with Push Notifications in Node.js using Service Workers
Node.js vs Nuxt - The Key Differences
Sharing Code Between Projects: Lessons Learned In The Trenches
Introduction to Express.js
Debugging a Node.js Application running in a Docker Container
Node.js Structural Comparisons
Creating Node.js Application Using Express Generator