API Gateway Pattern
Implement API Gateway pattern for routing, authentication, rate limiting, and service aggregation. This is a foundational concept in server-side JavaScript development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world Node.js experience. Take your time with each section and practice the examples
API Gateway Benefits
An API Gateway acts as a single entry point for all client requests. It handles cross-cutting concerns like authentication, rate limiting, logging, and request routing to appropriate microservices.. This is an essential concept that every Node.js developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
API Gateway Implementation
// API Gateway with Express and Node.js
const express = require('express');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const axios = require('axios');
const app = express();
// security middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
app.use(limiter);
// Service discovery
const services = {
users: 'http://localhost:3001',
orders: 'http://localhost:3002',
products: 'http://localhost:3003',
payments: 'http://localhost:3004'
};
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, 'your-secret-key', (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
};
// Request logging middleware
const logRequest = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path} - IP: ${req.ip}`);
next();
};
app.use(logRequest);
// Health check
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
services: Object.keys(services)
});
});
// User service routes
app.get('/api/users', authenticateToken, async (req, res) => {
try {
const response = await axios.get(`${services.users}/api/users`, {
headers: { 'Authorization': req.headers.authorization }
});
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Failed to fetch users'
});
}
});
app.get('/api/users/:id', authenticateToken, async (req, res) => {
try {
const response = await axios.get(`${services.users}/api/users/${req.params.id}`, {
headers: { 'Authorization': req.headers.authorization }
});
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Failed to fetch user'
});
}
});
// Order service routes
app.get('/api/orders', authenticateToken, async (req, res) => {
try {
const response = await axios.get(`${services.orders}/api/orders`, {
headers: { 'Authorization': req.headers.authorization }
});
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Failed to fetch orders'
});
}
});
// Aggregated endpoint - Get order with user and product details
app.get('/api/orders/:orderId/details', authenticateToken, async (req, res) => {
try {
const { orderId } = req.params;
// Get order details
const orderResponse = await axios.get(`${services.orders}/api/orders/${orderId}`, {
headers: { 'Authorization': req.headers.authorization }
});
const order = orderResponse.data;
// Get user details
const userResponse = await axios.get(`${services.users}/api/users/${order.userId}`, {
headers: { 'Authorization': req.headers.authorization }
});
const user = userResponse.data;
// Get product details for each product
const productPromises = order.products.map(product =>
axios.get(`${services.products}/api/products/${product.productId}`, {
headers: { 'Authorization': req.headers.authorization }
})
);
const productResponses = await Promise.all(productPromises);
const products = productResponses.map(response => response.data);
// Combine the data
const orderDetails = {
...order,
user,
products: order.products.map((orderProduct, index) => ({
...orderProduct,
details: products[index]
}))
};
res.json(orderDetails);
} catch (error) {
console.error('Error fetching order details:', error);
res.status(500).json({ error: 'Failed to fetch order details' });
}
});
// Product service routes
app.get('/api/products', async (req, res) => {
try {
const response = await axios.get(`${services.products}/api/products`);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Failed to fetch products'
});
}
});
// Payment service routes
app.post('/api/payments', authenticateToken, async (req, res) => {
try {
const response = await axios.post(`${services.payments}/api/payments`, req.body, {
headers: { 'Authorization': req.headers.authorization }
});
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Failed to process payment'
});
}
});
// Error handling middleware
app.use((error, req, res, next) => {
console.error('API Gateway Error:', error);
res.status(500).json({ error: 'Internal server error' });
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`API Gateway running on port ${PORT}`);
});