How to Deploy and Run Node-js Rest API on Minikube

How to Deploy and Run Node-js Rest API on Minikube

In this post, we can deploy and run simple NodeJS rest API on Minikube on our local machine.

Minikube is a tool that runs a single-node Kubernetes cluster in a virtual machine on your personal computer. We are going to deploy and run NodeJS rest API on MInikube in this article. First, we create an API and run it normally, then we will run the same on the Docker and, finally, we create a deployment and service objects to deploy and run it on Kubernetes locally.

We are going to need some pre-requisites to complete this project or run it on your local machine.

Example Project

Since this post is all about how we can run NodeJS API on local Kubernetes we are not going to focus much on API itself.

Here is a simple NodeJS API with two routes / and /name:myname returnsHello World and returning whatever the name you entered respectively.

const http = require('http');
const qs = require("querystring");
const url = require('url');

const port = process.env.PORT || 3000

const server = http.createServer((req, res) => {

    if (req.method !== 'GET') handleError(405, res);

    const {pathname, query} = url.parse(req.url);

    if (pathname === '/name') {
        const {name} = qs.parse(query);
        if (name) {
            res.end(`<h1>You Entered: ${name.toUpperCase()}</h1>`)
        } else {
            res.end("Bad Request");
        }
        
    }

    if (pathname === '/') {
        res.end(`<h1>Hello World</h1>`)
    }
});

function handleError(code, res) {
    res.statusCode = code;
    res.end(`{"error": "${http.STATUS_CODES[code]}"}`);
}

server.listen(port, () => {
    console.log(`Server listening on port ${port}`);
});

index.js

Here is the Github link. You can clone it and run it on your machine

// clone it
git clone https://github.com/bbachi/nodejs-restapi-minikube.git

// install and start the project
npm install
npm start
Run it On Docker

Docker is a container runtime that Kubernetes uses. You can use another container runtime but the Docker is most popular for now. Let’s run this Node API on Docker. We need to define Dockerfile first which is used to automate the Docker image creation. Docker builds images by reading instructions from the Dockerfile.

Here is the Dockerfile for this project. We are building the image from base image node:10 and copying the package.json to install all the dependencies. Copying just package.json to install all the dependencies is one of the best practices. Docker builds images from the layers in the cache. We don’t want to repeat installing if there is a change in the index.js.

# stage1 as builder
FROM node:10-alpine

WORKDIR /api

COPY . .

EXPOSE 3000

ENTRYPOINT ["node", "index.js"]

Dockerfile

Let’s build the image and run it on docker with these instructions.

// building the image
docker build -t node-api .

// list images
docker images

// Run the container
docker run -d --name nodeapi -p 3000:3000 node-api

Once you run the above commands you can hit these URLs in the browser. You can exec into a running container and see the file system inside the container.

// urls
http://localhost:3000/name?name=somename
http://localhost:3000/

// exec into the running container
docker exec -it nodeapi /bin/sh
Create a Deployment and Service Objects

A pod is a group of one or more containers that share the storage and network and has the specification on how to run the container. You can check the pod documentation here.

Running Local images on Minikube

Kubernetes always try to pull images from the Docker registry. If you want to use your local docker images you need to do certain steps on your local machine.

// set the environment
eval $(minikube docker-env)

// build the image
docker build -t node-api .

// list the images
docker images

Let’s create a pod with the below file. Before that, You need to start the Minikube on your local machine with this command minikube start and create a pod with this kubectl create -f pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: node-api-pod
spec:
  containers:
    - image: node-api
      name: node-api
      imagePullPolicy: Never
      resources: {}
      ports:
        - containerPort: 3000

pod.yml

You can exec into the file system with this command kubectl exec -it node-api-pod .bin/sh

exec into a running pod

Deployment

Creating just one pod is not enough and what if you want to scale out the application and want to run 10 replicas at the same time. What if you want to change the number of replicas depending on the demand. That’s where the deployment comes into the picture. We specify the desired state in the deployment object such as how many replicas you want to run etc.

Kubernetes makes sure that it always meets the desired state. It creates replica sets which inturn creates pods in the background. Let’s create a Deployment for our project with this command kubectl create -f deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nodeapi
  name: nodeapi
spec:
  replicas: 5
  selector:
    matchLabels:
      app: nodeapi
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nodeapi
    spec:
      containers:
      - image: node-api
        name: node-api
        imagePullPolicy: Never
        resources: {}
        ports:
          - containerPort: 3000 
status: {}

deployment.yml

We have 5 replicas in the specification and the deployment creates 5 pods and 1 replica set.

deployment

Service

Service is an abstract way to expose an application running on a set of Pods as a network service. Let’s create a service with type NodePort so that we can access the nodejs API from the browser. Here is the service object YAML

apiVersion: v1
kind: Service
metadata:
  name: nodeapi
  labels:
    run: nodeapi
spec:
  ports:
  - port: 3000
    protocol: TCP
  selector:
    app: nodeapi
  type: NodePort

service.yml

Create a service with this command kubectl create -f service.yml and you can list the service with this kubectl get svc

Running services

How to access deployment from the browser

We have created deployment and services and now we need to access this deployment from the browser.

We need to get the public IP address of the Kubernetes with this command kubectl cluster-info

master is running at 192.168.64.2

Get the port from the service object that we just created with this command kubectl get svc

service is listening on port 32329

Let’s access the nodejs API with this IP address 192.168.64.2 and port 32329 with the below URLs. Make sure that you use Http instead of Https.

// endpoints port and IP address might change

http://192.168.64.2:32329/name?name=myname

http://192.168.64.2:32329/

Here is the result

Accessing API in the browser

Conclusion

It’s always convenient to use Minikube and own docker images for the local testing. We don’t even deploy in some kind of environment to test our workflow.

Thank you for reading!

Creating a RESTful Web API with Node.js and Express.js from scratch

Creating a RESTful Web API with Node.js and Express.js from scratch

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

You can also build a Web API in Node.js by using another framework except Express.js but Express.js is one of the most popular web framework for Node.js.

You can found the final source code of this Web API in this github repository.

Let’s start to create our mentioned Web API.

Before start

If you have never used Node.js or npm package manager you should install them.

To check whether the Node.js is already installed on your computer, open your terminal and run node -v command. If you see your Node.js version it's installed. Otherwise go to below link.

Click here to download and install Node.js (You can choose LTS version)

And if you don’t have any IDE or text editor for writing javascript I advice you Visual Studio Code.

Click here to download VS Code (Optional)

About express-generator

In fact we could use <a href="https://expressjs.com/en/starter/generator.html" target="_blank">express-generator</a> tool which designed to creating an Express Web API quickly but I want to create this API from scratch because of that tool puts some extra files and folder structures that we don't need them now. But you can use this useful tool next time on creating new Web API. I won't use it now due to keep article simple.

Creating Project

Go to your workspace root folder and create a new folder there named "todo-api".

Then create "package.json" and "server.js" files into "todo-api" folder like below.

package.json

{
    "name": "todo-api",
    "version": "1.0.0",
    "scripts": {
        "start": "node server.js"
    },
    "dependencies": {
        "express": "^4.16.4"
    }
}

server.js

const http = require('http');
const express = require('express');
const app = express();
app.use(express.json());
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

After creating above files open your terminal in the "todo-api" folder and run npm installcommand.

This command will be install your project dependencies which pointed at the "package.json" file.

After finished package download process, downloaded dependency files will be installed into"node_modules" folder at the root of the "todo-api" folder.

After finished package installing then run npm start to start our Web API.

Now our Web API listening. To see result open your web browser then write localhost:3000 to address bar and press enter.

As result you’ll see our request handler response in your browser: “todo api works”.

This is a dead simple Express.js Web API. And it needs the some development. For example we need to an api endpoint to get todo items. So let’s add a new API endpoint for this.

Create a new folder named "routes" in the root of the "todo-api" folder.

Then create a "items.js" file inside of "routes" folder and put following codes inside it.

Your final folder structure should be like below;

/todo-api
/node_modules
/routes
    items.js
package.json
server.js

items.js

const express = require('express');
const router = express.Router();
const data = [
    {id: 1, title: 'Finalize project', order: 1, completed: false, createdOn: new Date()},
    {id: 2, title: 'Book ticket to London', order: 2, completed: false, createdOn: new Date()},
    {id: 3, title: 'Finish last article', order: 3, completed: false, createdOn: new Date()},
    {id: 4, title: 'Get a new t-shirt', order: 4, completed: false, createdOn: new Date()},
    {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];
router.get('/', function (req, res) {
    res.status(200).json(data);
});
router.get('/:id', function (req, res) {
    let found = data.find(function (item) {
        return item.id === parseInt(req.params.id);
    });
    if (found) {
        res.status(200).json(found);
    } else {
        res.sendStatus(404);
    }
});
module.exports = router;

Initial code of "items.js" file contains two endpoints. First one gets all todo items and second one gets one item which matches given id parameter.

Before testing items routes we should register it in the "server.js" file.

Modify "server.js" file like below to register new item routes.

server.js

const http = require('http');
const express = require('express');
const itemsRouter = require('./routes/items');
const app = express();
app.use(express.json());
app.use('/items', itemsRouter);
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

Now run npm start to start our Web API.

Then open your web browser and write localhost:3000/items to address bar and press enter.

You’ll see todo items json array in the response body.

And write localhost:3000/items/3 to address bar and press enter.

You’ll see the todo item which has id 3 in the response body.

But not finished up yet.

CRUD Operations and HTTP methods

I think we’ll need CRUD operations to Create, Read, Update and Delete todo items.

We have already two endpoints for getting items. So we need Create, Update and Delete endpoints.

Let’s add also these endpoints into the items.js file.

Our final "items.js" file and endpoints should be like below.

const express = require('express');
const router = express.Router();

const data = [
  {id: 1, title: 'Finalize project',          order: 1, completed: false, createdOn: new Date()},
  {id: 2, title: 'Book ticket to London',     order: 2, completed: false, createdOn: new Date()},
  {id: 3, title: 'Finish last article',       order: 3, completed: false, createdOn: new Date()},
  {id: 4, title: 'Get a new t-shirt',         order: 4, completed: false, createdOn: new Date()},
  {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];

router.get('/', function (req, res) {
  res.status(200).json(data);
});

router.get('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    res.status(200).json(found);
  } else {
    res.sendStatus(404);
  }
});

router.post('/', function (req, res) {
  let itemIds = data.map(item => item.id);
  let orderNums = data.map(item => item.order);

  let newId = itemIds.length > 0 ? Math.max.apply(Math, itemIds) + 1 : 1;
  let newOrderNum = orderNums.length > 0 ? Math.max.apply(Math, orderNums) + 1 : 1;

  let newItem = {
    id: newId,
    title: req.body.title,
    order: newOrderNum,
    completed: false,
    createdOn: new Date()
  };

  data.push(newItem);

  res.status(201).json(newItem);
});

router.put('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let updated = {
      id: found.id,
      title: req.body.title,
      order: req.body.order,
      completed: req.body.completed
    };

    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1, updated);

    res.sendStatus(204);
  } else {
    res.sendStatus(404);
  }
});

router.delete('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1);
  }

  res.sendStatus(204);
});

module.exports = router;

Short Explanation

I wanna explain shortly some points of our last codes.

First of all you must have noticed that our api works on a static data and keeps it on memory. All of our GET, POST, PUT and DELETE http methods just manipulate a json array. The purpose of this is to keep article simple and draw attention to the Web API structure.

Due to this situation our POST method has some extra logic such as calculating next item ids and order numbers.

So you can modify logic and data structures in these http methods to use a database or whatever you want.

Testing API with Postman

We have tested the GET methods of our Web API in our web browser and seen responses. But we can’t test directly POST, PUT and DELETE http methods in web browser.

If you want to test also other http methods you should use Postman or another http utility.

Now I’ll show you how to test the Web API with Postman

Before we start click here and install Postman.

When you first launch Postman after installing you’ll see start window. Close this start window by clicking close button on top right corner. Then you must see following screen.

An empty Postman request

Sending GET Request

Before sending a request to API we should start it by running npm startcommand as we do before.

After start the Web API and seeing “Server listening on…” message write localhost:3000/itemsto address bar as seen below and click Send button. You'll see todo items array as API response like below.

Sending a GET request with Postman

You can try similarly by giving an item id in request url like this localhost:3000/items/3

Sending POST Request

To sending a POST request and create a new todo item write localhost:3000/items to address bar and change HTTP verb to POST by clicking arrow at front of the address bar as seen below.

Sending a POST request with Postman

Before sending the POST request you should add request data to body of the request by clicking body tab and selecting raw and JSON as seen below.

Attaching a JSON body to POST request in Postman

Now click Send button to send POST request to the Web API. Then you must get “201 Created” http response code and seeing created item in the response body.

To see the last status of todo items send a get request to localhost:3000/itemsaddress. You must see newly created item at the end of the list.

Sending PUT Request

Sending PUT request is very similar to sending POST request.

The most obvious difference is request url should be pointed specific item like this localhost:3000/items/3

And you should choose PUT as http verb instead of POST and send all of the required data in the request body unlike POST.

For example you could send a JSON body in the PUT request as below.

An example JSON body for PUT request

{
    "title": "New title of todo item",
    "order": 3,
    "completed": false
}

When you click Send button you must get “204 No Content” http response code. You can check item you updated by sending a get request.

Sending DELETE Request

To send a DELETE request, change the request url to address a specific item id like this localhost:3000/items/3

And select DELETE as http verb and click Send button.

You must get “204 No Content” http response code as result of the DELETE operation.

Send a get request and see the last status of list.

About the DELETE Http Request

I want to say a few words about DELETE http request. You must have noticed something in our delete code. DELETE request returns “204 No Content” every situation.

Http DELETE requests are idempotent. So what that mean? If you delete a resource on server by sending DELETE request, it’s removed from the collection. And every next DELETE request on the same resource won’t change outcome. So you won’t get “404 Not Found” in the second request. Each request returns same response whether succeed or not. That’s mean idempotent operation.

Conclusion

Finally we’ve tested all http methods of our Web API.

As you can see, it works just fine.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

How to Create a Fake REST API Server using Node.js

How to Create a Fake REST API Server using Node.js

How to make a REST API Server using Node.js for testing your AJAX client side.

In this post, I want to show you how to create a fake REST API Server, using Node.js. This will be just a Server to test AJAX at your client side. In addition to it, it added support for CORS and request verbs like (POST, GET, DELETE, PUT).

First of all, create a folder (RESTAPI)  and create a JSON file, as shown below. I used the file mentioned in the link above for sample JSON data.

{  
   "user1" : {  
      "name" : "mahesh",  
      "password" : "password1",  
      "profession" : "teacher",  
      "id": 1  
   },  
   "user2" : {  
      "name" : "suresh",  
      "password" : "password2",  
      "profession" : "librarian",  
      "id": 2  
   },  
   "user3" : {  
      "name" : "ramesh",  
      "password" : "password3",  
      "profession" : "clerk",  
      "id": 3  
   }  
}  

From start menu, open Node.Js command prompt and change the directory, where our folder is located and type the command given below.

npm install express --save

Create a JavaScript file, add the piece of code and name it server.js.

var express = require('express');  
var app = express();  
var fs = require("fs");  
var bodyParser = require('body-parser');  
  
//enable CORS for request verbs
app.use(function(req, res, next) {  
  res.header("Access-Control-Allow-Origin", "*");  
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");  
  res.header("Access-Control-Allow-Methods","POST, GET, PUT, DELETE, OPTIONS");  
  next();  
});  
  
app.use(bodyParser.urlencoded({  
    extended: true  
}));  
  
app.use(bodyParser.json());  
  
//Handle GET method for listing all users
app.get('/listUsers', function (req, res) {  
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {  
       console.log( data );  
       res.end( data );  
   });  
})  
  
//Handle GET method to get only one record
app.get('/:id', function (req, res) {  
   // First read existing users.  
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {  
       users = JSON.parse( data );  
       console.log(req.params.id);  
       var user = users["user" + req.params.id]   
       console.log( user );  
       res.end( JSON.stringify(user));  
   });  
})  
  
//Handle POST method
app.post('/addUser', function (req, res) {  
   // First read existing users.  
       fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {  
       var obj = JSON.parse('[' + data + ']' );  
       obj.push(req.body);  
       console.log(obj);  
         
       res.end( JSON.stringify(obj)  );  
   });  
})  
  
//Handle DELETE method
app.delete('/deleteUser/:id', function (req, res) {  
  
   // First read existing users.  
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {  
       data = JSON.parse( data );  
         
       delete data["user" + req.params.id];  
         
       console.log( data );  
       res.end( JSON.stringify(data));  
   });  
})  
  
//Handle GET method
app.put('/updateUser/:id', function(req,res){  
      
    // First read existing users.  
    fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {  
       //var obj = JSON.parse('[' + data + ']' );  
       data = JSON.parse( data );  
       var arr={};  
       arr=req.body;  
         
        data["user" + req.params.id]= arr[Object.keys(arr)[0]] ; //  req.body;   //obj[Object.keys(obj)[0]]  
          
        res.end( JSON.stringify( data ));  
         
    });  
} );  
  
var server = app.listen(8081, function () {  
  
  var host = server.address().address  
  var port = server.address().port  
  
  console.log("Example app listening at http://%s:%s", host, port)  
  
})  

Notice

The parameter (data) of the request handler method is supposed to be an array of JSON objects but it didn't accept the normal array methods like (push) method, so I decided to parse it with the brackets [ ].

var obj = JSON.parse('[' + data + ']' );     

To run this Server, open node.js command prompt and run the command.

$ node server.js

Now, the Server is running and now you have a REST API Server, which supports CORS for the requests.

Here, it is listening at port 8081. To test our Server, this is the sample data for the testing purpose.

Here, it is listening at port 808. To test our Server, this is a sample data for the testing purpose.

I hope it comes in handy. Thank you for reading !

Scaling Node.js Applications with Kubernetes and Docker

Scaling Node.js Applications with Kubernetes and Docker

Scaling Node.js Applications with Kubernetes and Docker. We will explore the benefits of DevOps process using Kubernetes, Docker, and Node.js. Learn about the basics of Kubernetes and tips to scale Node.js Applications. Learn the common problems that we face when we decide to change from monoliths to microservices using Docker and JavaScript.

We will explore the benefits of DevOps process using Kubernetes, Docker, and Node.js. Showing how Docker and Node.js can work together, using the power of Kubernetes to release and to scale automatically stateless services. At this talk we will explore the key concepts and components to start working with Kubernetes, real scenarios and the differences between the traditional approach compared to Container based applications. Attendees will learn about the basics of Kubernetes and tips to scale Node.js applications furthermore they will learn the common problems that we face when we decide to change from monoliths to microservices using Docker and JavaScript.

What are the key takeaways from this talk?

  • Service communication
  • Kubernetes and Docker,
  • High availability & release process