??? Building Microservices with Node.js: Communication Patterns and API Gateway Implementation:
Dhruv Patel
"MEAN Stack Developer | Full-Stack Web Applications | Specialist in E-commerce, HRMS & Real-time Systems"
Core Microservice Architecture :-
// User Service (users-service/index.js)
const express = require('express');
const app = express();
app.get('/api/users/:id', async (req, res) => {
try {
const user = await db.findUserById(req.params.id);
res.json(user);
} catch (error) {
res.status(500).send('User service error');
}
});
app.listen(3001, () => console.log('User service running on port 3001'));
// Order Service (orders-service/index.js)
const express = require('express');
const app = express();
app.get('/api/orders/:userId', async (req, res) => {
try {
const orders = await db.findOrdersByUserId(req.params.userId);
res.json(orders);
} catch (error) {
res.status(500).send('Order service error');
}
});
app.listen(3002, () => console.log('Order service running on port 3002'));
?? Inter-Service Communication Patterns
1. Synchronous REST Communication
// Order service calling User service
const axios = require('axios');
async function getUserDetails(userId) {
try {
const response = await axios.get(`https://user-service:3001/api/users/${userId}`);
return response.data;
} catch (error) {
console.error('Error fetching user details:', error);
throw new Error('Failed to fetch user details');
}
}
2. Asynchronous Message Queue Communication
// Using RabbitMQ for event-based communication
const amqp = require('amqplib');
async function publishOrderCreated(order) {
const connection = await amqp.connect('amqp://rabbitmq');
const channel = await connection.createChannel();
const exchange = 'order_events';
await channel.assertExchange(exchange, 'topic', { durable: true });
channel.publish(
exchange,
'order.created',
Buffer.from(JSON.stringify(order))
);
console.log('Order created event published');
setTimeout(() => {
connection.close();
}, 500);
}
// Consuming events in another service
async function setupOrderCreatedConsumer() {
const connection = await amqp.connect('amqp://rabbitmq');
const channel = await connection.createChannel();
const exchange = 'order_events';
await channel.assertExchange(exchange, 'topic', { durable: true });
const { queue } = await channel.assertQueue('', { exclusive: true });
await channel.bindQueue(queue, exchange, 'order.created');
channel.consume(queue, (msg) => {
if (msg !== null) {
const order = JSON.parse(msg.content.toString());
console.log('Received order created event:', order);
// Process the order...
channel.ack(msg);
}
});
}
3. Service Discovery
// Using Netflix Eureka with eureka-js-client
const Eureka = require('eureka-js-client').Eureka;
const client = new Eureka({
instance: {
app: 'order-service',
hostName: 'localhost',
ipAddr: '127.0.0.1',
port: {
'$': 3002,
'@enabled': true,
},
vipAddress: 'order-service',
dataCenterInfo: {
'@class': 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo',
name: 'MyOwn',
},
},
eureka: {
host: 'eureka-server',
port: 8761,
servicePath: '/eureka/apps/',
},
});
client.start();
?? API Gateway Implementation
// API Gateway using Express Gateway
// gateway.config.yml
http:
port: 8080
admin:
port: 9876
host: localhost
apiEndpoints:
users:
host: localhost
paths: '/users/*'
orders:
host: localhost
paths: '/orders/*'
serviceEndpoints:
usersService:
url: 'https://localhost:3001/'
ordersService:
url: 'https://localhost:3002/'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
pipelines:
users:
apiEndpoints:
- users
policies:
- rate-limit:
- action:
max: 100
windowMs: 60000
- proxy:
- action:
serviceEndpoint: usersService
changeOrigin: true
orders:
apiEndpoints:
- orders
policies:
- oauth2:
- action:
jwtSecret: 'shared-secret'
- proxy:
- action:
serviceEndpoint: ordersService
changeOrigin: true
API Gateway with Node.js and Express
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const rateLimit = require('express-rate-limit');
const jwt = require('express-jwt');
const app = express();
// Authentication middleware
const authenticate = jwt({
secret: process.env.JWT_SECRET,
algorithms: ['HS256']
});
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
// Routing and proxying
app.use('/api/users',
limiter,
createProxyMiddleware({
target: 'https://user-service:3001',
changeOrigin: true,
pathRewrite: {'^/api/users': '/api/users'}
})
);
app.use('/api/orders',
authenticate, // Protect orders with JWT
createProxyMiddleware({
target: 'https://order-service:3002',
changeOrigin: true,
pathRewrite: {'^/api/orders': '/api/orders'}
})
);
// Error handling for auth failures
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: 'Invalid or missing token' });
}
next(err);
});
app.listen(8000, () => {
console.log('API Gateway running on port 8000');
});
?? Best Practices for Node.js Microservices
?? Monitoring Microservices
// Using Prometheus with prom-client
const express = require('express');
const client = require('prom-client');
const app = express();
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics({ timeout: 5000 });
// Create a custom counter
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'path', 'status']
});
// Middleware to increment the counter
app.use((req, res, next) => {
res.on('finish', () => {
httpRequestsTotal.inc({
method: req.method,
path: req.path,
status: res.statusCode
});
});
next();
});
// Expose metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
app.listen(3001, () => console.log('Metrics server running on port 3001'));
?? What microservice challenges have you faced in your Node.js projects?
Share your experiences with scaling, communication patterns, or gateway implementations!
#nodejs #microservices #javascript #webdevelopment #architecture #backend #programming