Editor’s note: This article was last updated on 19 October 2022 to include additional information about microservice communication.
In early software development, best practice involved tightly coupling all of the software’s components in what is known as a monolithic application. However, monolithic applications require extra effort to respond to changes in the system. If any particular component develops a fault, the entire system is affected.
Nowadays, we can solve this problem by using microservices, which allow us to build our software components separately. A fault in one component won’t affect the functionality of the entire software product.
In this article, we’ll explore microservices, learning how to implement a microservice with Node.js and discussing how microservices are changing the practice of software development. Let’s get started!
To follow along with this article, you’ll need the following:
A monolithic application is a single-tiered application in which all components are composed as one unit.
Imagine you’re building a library management system, and all of the components, like books
and users
, and their respective services and databases are fused together as one unit. A fault in any one component will require bringing down the entire system to correct the error.
For this reason, monolithic applications are neither flexible nor easily scalable; you cannot build features concurrently or achieve continuous deployment. While monolithic applications are not cost-effective to manage, they are cost-effective to build. Therefore, developers recognized the need to create a system in which one faulty component wouldn’t affect the entire software system.
Microservices became necessary due to the shortcomings of the monolithic pattern of software development. In a microservice, each software application feature is separated from the other, in most cases with their respective servers and databases. Applications built with this kind of architecture are loosely coupled, also referred to as distributed applications.
Imagine we’re building an ecommerce store. We’ll need models for a payment feature
, cart
, customers
, admin
, and order
. Each of these features will have its own separate servers and databases.
Our ecommerce microservices will communicate with each other using the REST API framework. Because we’ll develop our store features independently from each other, if our system develops a fault, we can easily identify which feature to debug and avoid having to bring down the entire application.
In contrast to monolithic applications, applications developed using microservices are scalable. You can use any programming language to develop a microservice; in fact, you can use different languages to develop different features in a microservice application.
Overall, microservices offer a better developer experience. A new developer joining the team won’t have to understand the entire codebase, but rather just the features they are working on, increasing overall productivity. Lastly, unit testing is encouraged in microservices; you can write a unit test to test any particular functionality.
It’s important to keep in mind that building a microservice requires expertise because integration and end-to-end testing can be very challenging. Additionally, microservices can become very bulky, resulting in high maintenance costs. Lastly, it’s not always easy to migrate software already developed using monolithic architecture to a microservice, and it can be challenging for applications to locate each other within a complex network.
Choosing a microservice architectural pattern comes with some challenges; one of these is service-to-service communication. Services are a loosely coupled part of an application that together contribute to the application’s overall performance.
To achieve effective performance, there has to be a means of communication between the microservices. In a microservice application, communication is made possible through an inter-service communication protocol like HTTP(s), gRPC, or message brokers.
Let’s review some of the ways in which services establish communication in a microservice architecture.
HTTP communication is a kind of synchronous communication pattern where a service is dependent on another to perform:
The image above represents the HTTP request-response cycle, where the client makes a request and waits for a response from the server-side application.
The event-driven communication pattern entails an interaction between a service provider and a service consumer. The service consumer requires a resource from an external source. It then performs some computations and relays the response to the client:
You can use any programming language to develop a microservice, like Java, C#, or Python, but Node.js is an outstanding choice for a few reasons.
For one, Node.js uses an event-driven architecture and enables efficient, real-time application development. Node.js single-threading and asynchronous capabilities enable a non-blocking mechanism. When building a microservice with Node.js, developers will experience an uninterrupted flow and enjoy Node’s speed, scalability, and easy maintenance.
To illustrate how to develop microservices with Node.js, we’ll use the OpenWeather API service. First, create a free account.
Create a new folder on your computer, preferably on your desktop for easy access, and name it weathermicroservice
. Open weathermicroservice
in your code editor and confirm that you have Node.js installed on your computer by running the command below:
node -v
If Node.js is not installed, go ahead and download it. In weathermicroservice
, run the command below to initialize the package.json
:
Run npm init or npm init -y
With npm init
, you can customize the setting or fields to create the package.json
file. On the other hand, npm init -y
uses the default setting or fields to create the package.json
file.
Now, let’s install the required dependencies with the command below:
run npm install express nodemon body-parser
Your package.json
file should look similar to the screenshot below:
Directly inside the main folder, create a file called server.js
. Inside, write the following code:
const express = require("express"); const bodyParser = require("body-parser") const aboutRouter = require("./routes/about"); const weatherRouter = require("./routes/weather"); const PORT = 3000; const HOST_NAME = "localhost"; const app = express(); app.use(express.static("client")); app.use(bodyParser.urlencoded({extended: true})); app.use("/weather", weatherRouter); app.use("/about", aboutRouter); app.listen(PORT, HOST_NAME, ()=>{ console.log(`Server running at ${HOST_NAME}:${PORT}`) })
server.js
is the main file for our basic weather microservice app, as indicated in our package.json
file. We’ll create another folder inside the weathermicroservice
folder named routes
.
Inside the routes
folder, create two files named about.js
and weather.js
. Inside the about.js
file, write the following code:
const express = require("express"); const properties = require("../package.json"); const aboutRoute = express.Router(); aboutRoute.get("/", (req, res)=>{ const aboutInfo ={ name: properties.name, description: properties.description, author: properties.author } res.json(aboutInfo) }) module.exports = aboutRoute
In the code above, we first require
or import the express
module, followed by the package.json
file, which we assign to the variable named properties
.
Then, we invoke the express.Router
function. Finally, we implement our GET
route, which returns an aboutInfo
containing information about our application, as contained in the package.json
file. Remember, we imported our package.json
file on the second line of code in the about.js
file.
Now that we’re done with the about.js
route module, we’ll create the route that handles the weather info
request. Before we do that, let’s create the client-side of our application.
First, we’ll create a basic HTML file that receives user input. Under your main project folder, create a folder called client
. Inside it, create an index.html
file and add the code below to it:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Weather App</title> </head> <body> <h1>A basic Weather app microservice</h1> <p>Enter your details to get weather Info </p> <form action="/weather" method="post"> <label for="cityInput">City name: </label> <input type="text" name="cityName" placeholder="Enter name of your city" id="cityInput"> <label for="unitInput">Unit</label> <input type="text" name="unit" placeholder="Enter unit in metric" id="unitInput"> <button type="submit">submit</button> </form> </body> </html>
This is a basic HTML form that allows you to input your city name and metric units, as well as submit the inputs as a POST
request to the weather route.
To create the route that handles the weather info, create a file called weather.js
inside the route
folder and add the following code to it:
const express = require("express"); const https = require('https') const weatherRoute = express.Router(); weatherRoute.get("/", (req, res)=>{ res.sendFile(__dirname, + "index.html") }) weatherRoute.post("/", (req, res)=>{ const city = req.body.cityName const appiKey = "Your API Key" const unit = req.body.unit const url = "https://api.openweathermap.org/data/2.5/weather?q="+ city + "&appid="+appiKey+"&units="+unit+"" https.get(url, (response)=>{ response.on("data", (chunk)=>{ const responseData = JSON.parse(chunk); const temperature = responseData.main.temp; const weatherDes = responseData.weather[0].description; const icon = responseData.weather[0].icon; const imageURL = "http://openweathermap.org/img/wn/"+ icon + "@2x.png"; const cityName = responseData.name; res.write(`<h1>The weather is ${temperature} degree celsius in ${cityName} and the description is ${weatherDes} </h1>`) res.write("<img src="+ imageURL +">") res.send() }) }) }) module.exports = weatherRoute
First, we required Express
and the native https
Node.js modules. We then created an instance of Express and invoked the Express router function. Using the Express instance, we created our first route that sends a response to the client when a request hits the server.
The response should serve a basic form like the one below to the client:
res.sendFile(__dirname, + "index.html")
We then send the index.html
file to the client. When the submit
button is clicked, a POST
request is made by the client to the server. The server accesses the body of the request through the request
object and saves it into the variables:
const city = req.body.cityName const unit = req.body.unit
Now that we have these parameters, we can easily access the OpenWeather API. The URL below helps us connect to the Weather API:
const url = "https://api.openweathermap.org/data/2.5/weather?q="+ city + "&appid="+appiKey+"&units="+unit+""
Using the Node.js native https
module, we perform a GET
request to the Weather API. We then parse
the data from the response body into an object using JSON.parse()
.
We’ll need the following as part of the response data for our application:
weatherDes
Finally, we used the res.write
function from the Node.js writeable interface
to send the response back to the client. Now, to run this application, run the following command in your root folder:
node server.js
On your console, you should have something similar to the image below:
Then, go to your browser, Chrome preferably, and make a request to port 3000
:
You should have a response like the one below:
In the City name
field, type in the name of your city, and in the Unit
field, type in metric
to convert the default temperature unit to celsius
. The default temperature unit in the Weather API is Kelvin
.
If you do everything correctly, then you have a response with the temperature reading in your city, and the icon to represent it:
The image above is the output I received representing the temperature reading in my city.
In this tutorial, we learned about the important differences between microservices and monolithic applications. We learned why Node.js is a great choice for building microservices, and we ran through an example using the OpenWeather API.
Using a microservice offers flexibility and performance benefits that can’t be achieved with a monolithic application. The event-driven architecture of Node.js makes it a perfect choice for microservices, being fast, highly scalable, and easy to maintain.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowNitro.js is a solution in the server-side JavaScript landscape that offers features like universal deployment, auto-imports, and file-based routing.
Ding! You got a notification, but does it cause a little bump of dopamine or a slow drag of cortisol? […]
A guide for using JWT authentication to prevent basic security issues while understanding the shortcomings of JWTs.
Auth.js makes adding authentication to web apps easier and more secure. Let’s discuss why you should use it in your projects.
9 Replies to "Building microservices with Node.js"
Bro, you should attach your website and linkedin profile to your page. That way people can reach you with opportunties.
Thank you very much for this, I will try to include at least a social media link.
Toi bad the most important part is not described. How do multiples microservice interact with each other. For exemple you call an endpoint this endpoint retreive data and call 2 other micro services that have coupled data in another db
Sorry about this, there is an existing article that explain communication between microservices here on logRocket blog, you can please read up here https://blog.logrocket.com/methods-for-microservice-communication/
Thank you.
For a Node.js article, this felt pretty dated and out of touch. I guess my biggest gripe is that you’re promoting the use of request (which has been deprecated for a while now) but also some of the conventions used aren’t exactly the best. I also wish you’d explain microservices more in depth. Just some constructive criticism
The code snippets are all with mistakes. I don’t know why, but LogRocket has all the best tutorials to start with but with a lot of mistakes. This is very bad
Hi John, thank you for the comment, do you mind pointing out the mistake?
In the server.js file, the line app.use(express.static(“client”)) should be removed. It is not needed in this code snippet and will cause an error.
In the weather.js file, the line weatherRoute.get(“/”, (req, res)=>{res.sendFile(__dirname, + “index.html”)} will cause an error because __dirname is a path, and res.sendFile expects a file path as its first argument, but it is being concatenated with a string “index.html”. It should be like this res.sendFile(__dirname + “/index.html”)
In the weather.js file, the line const appiKey = “Your API Key” is hardcoded and it should be replaced with a valid API key otherwise the application will not work as expected.
In the weather.js file, the line const url = “https://api.openweathermap.org/data/2.5/weather?q=”+ city + “&appid=”+appiKey+”&units=”+unit+”” should use encodeURI function to encode the url parameters, to avoid any issues with special characters or spaces in the parameters.
youre the real chad saved my day thank you broo