Because microservices architecture is flexible, scalable, and easy to maintain, it is becoming increasingly popular for creating large-scale systems. In contrast to monolithic designs, which combine all functionality into a single codebase, microservices divide an application into more manageable, separate services that may interact with one another. In this blog article, we will look at how to create microservices using Node.js, a well-liked runtime environment for building effective and scalable applications. Let’s learn about Microservice using Node.js.
What are Microservices?
The architectural approach known as “microservices” is characterized by creating individual services as separate, loosely connected entities inside an application. Every service may be deployed individually, operates in its process, and usually carries out a single business function. Services can exchange messages via messaging queues or lightweight protocols like HTTP/REST.
Key benefits of microservices include:
- Scalability: Individual services can be scaled based on demand.
- Resilience: If one service fails, others remain unaffected.
- Maintainability: Small, independent services are easier to maintain and update.
Why Microservices Using Node.js?
Node.js is well-suited for microservices architecture because:
- Non-blocking I/O: Node.js is designed to handle asynchronous, event-driven operations, which is crucial for microservices that must handle multiple services and data flows efficiently.
- Lightweight and fast: Node.js is known for its lightweight, quick, and scalable nature, making it a great choice for microservice-based systems.
- NPM Ecosystem: The Node Package Manager (NPM) offers a wide range of libraries that make building microservices quicker and easier.
Steps to Build Microservices with Node.js
1. Setting Up the Environment
First, ensure you have Node.js and NPM installed. To verify, run:
node -v npm -v
Create a directory for your project:
mkdir microservices-demo cd microservices-demo
Initialize the project:
npm init -y
Install the necessary packages, such as Express for handling HTTP requests and Docker (if needed) for containerizing services:
npm install express
2. Design the Microservices
Before diving into code, you must plan the services you’ll build. For this demo, let’s create two microservices:
- User Service: Manages user-related data.
- Order Service: Manages orders placed by users.
Each service will run independently and communicate with each other via REST APIs.
3. Building the User Service
Let’s start by creating the User Service. Create a folder named user-service
and inside it, initialize a basic Express server:
mkdir user-service cd user-service npm init -y npm install express
Create server.js
:
const express = require('express'); const app = express(); app.use(express.json()); const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]; // Get all users app.get('/users', (req, res) => { res.json(users); }); // Get user by ID app.get('/users/:id', (req, res) => { const user = users.find(u => u.id == req.params.id); if (user) { res.json(user); } else { res.status(404).json({ message: 'User not found' }); } }); const PORT = 3000; app.listen(PORT, () => { console.log(`User service running on port ${PORT}`); });
Run the service:
node server.js
The User Service will now be running on localhost:3000
.
4. Building the Order Service
Now, let’s create the Order Service. Create a folder order-service
:
mkdir order-service cd order-service npm init -y npm install express
Create server.js
:
const express = require('express'); const app = express(); app.use(express.json()); const orders = [ { id: 1, userId: 1, product: 'Laptop' }, { id: 2, userId: 2, product: 'Phone' } ]; // Get all orders app.get('/orders', (req, res) => { res.json(orders); }); // Get order by user ID app.get('/orders/:userId', (req, res) => { const userOrders = orders.filter(order => order.userId == req.params.userId); if (userOrders.length > 0) { res.json(userOrders); } else { res.status(404).json({ message: 'No orders found for this user' }); } }); const PORT = 3001; app.listen(PORT, () => { console.log(`Order service running on port ${PORT}`); });
Run the service:
node server.js
The Order Service will now be running on localhost:3001
.
5. Communication Between Services
In a microservices architecture, services need to communicate. One common way is via HTTP REST. For instance, if the Order Service needs to fetch user details from the User Service, it can make a REST API call.
Here’s how you can modify the Order Service to fetch user details:
In server.js
of the Order Service:
const axios = require('axios'); // Get order by user ID with user details app.get('/orders/:userId', async (req, res) => { const userOrders = orders.filter(order => order.userId == req.params.userId); if (userOrders.length > 0) { try { const userResponse = await axios.get(`http://localhost:3000/users/${req.params.userId}`); const user = userResponse.data; res.json({ user: user, orders: userOrders }); } catch (error) { res.status(500).json({ message: 'Error fetching user details' }); } } else { res.status(404).json({ message: 'No orders found for this user' }); } });
Here, we used axios
to make an HTTP request to the User Service to fetch user details. To install axios
, run:
npm install axios
Now, when you access the Order Service, it will include the user details along with their orders.
6. Containerizing Services with Docker
Once your services are built, containerization with Docker ensures that they run consistently across different environments.
Create a Dockerfile
for each service:
For User Service (user-service/Dockerfile
):
FROM node:14 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["node", "server.js"]
For Order Service (order-service/Dockerfile
):
FROM node:14 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3001 CMD ["node", "server.js"]
Build and run the containers:
docker build -t user-service ./user-service docker build -t order-service ./order-service docker run -p 3000:3000 user-service docker run -p 3001:3001 order-service
Conclusion
By dividing services into smaller, independent components, Node.js microservices enable you to build scalable, maintainable apps. You can build effective microservices that scale on demand and can interact with ease by using tools like Docker and frameworks like Express. This design is perfect for contemporary, large-scale applications because it provides more flexibility and robustness.