Master the art of building and deploying microservices using Node.js and Kubernetes. Learn how to create scalable, resilient, and maintainable distributed systems. 🚀
Project Structure 📁
# Initialize project
mkdir microservices-demo
cd microservices-demo
# Create services
mkdir -p services/{auth,users,products,orders}
Authentication Service 🔐
// services/auth/src/server.ts
import express from 'express';
import jwt from 'jsonwebtoken';
import { z } from 'zod';
import { createClient } from 'redis';
const app = express();
app.use(express.json());
const redis = createClient({
url: process.env.REDIS_URL
});
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
app.post('/auth/login', async (req, res) => {
try {
const { email, password } = loginSchema.parse(req.body);
// In production, validate against secure database
const user = await validateUser(email, password);
if (!user) {
return res.status(401).json({
error: 'Invalid credentials'
});
}
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET!,
{ expiresIn: '1h' }
);
await redis.set(
`token:${token}`,
JSON.stringify(user),
{ EX: 3600 }
);
res.json({ token, user });
} catch (error) {
res.status(400).json({
error: 'Invalid request'
});
}
});
app.post('/auth/logout', async (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (token) {
await redis.del(`token:${token}`);
}
res.status(204).send();
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Auth service running on port ${PORT}`);
});
User Service 👥
// services/users/src/server.ts
import express from 'express';
import { PrismaClient } from '@prisma/client';
import { authenticateRequest } from './middleware/auth';
import { z } from 'zod';
const prisma = new PrismaClient();
const app = express();
app.use(express.json());
const userSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
role: z.enum(['USER', 'ADMIN']).default('USER')
});
app.get('/users', authenticateRequest, async (req, res) => {
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true
}
});
res.json(users);
});
app.post('/users', authenticateRequest, async (req, res) => {
try {
const userData = userSchema.parse(req.body);
const user = await prisma.user.create({
data: userData
});
res.status(201).json(user);
} catch (error) {
res.status(400).json({
error: 'Invalid user data'
});
}
});
app.get('/users/:id', authenticateRequest, async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
include: {
orders: true
}
});
if (!user) {
return res.status(404).json({
error: 'User not found'
});
}
res.json(user);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Users service running on port ${PORT}`);
});
Product Service 🛍️
// services/products/src/server.ts
import express from 'express';
import { PrismaClient } from '@prisma/client';
import { authenticateRequest } from './middleware/auth';
import { z } from 'zod';
const prisma = new PrismaClient();
const app = express();
app.use(express.json());
const productSchema = z.object({
name: z.string().min(2),
description: z.string(),
price: z.number().positive(),
stock: z.number().int().min(0),
category: z.string()
});
app.get('/products', async (req, res) => {
const { category, minPrice, maxPrice } = req.query;
const products = await prisma.product.findMany({
where: {
category: category ? String(category) : undefined,
price: {
gte: minPrice ? Number(minPrice) : undefined,
lte: maxPrice ? Number(maxPrice) : undefined
}
},
include: {
reviews: true
}
});
res.json(products);
});
app.post('/products', authenticateRequest, async (req, res) => {
try {
const productData = productSchema.parse(req.body);
const product = await prisma.product.create({
data: productData
});
res.status(201).json(product);
} catch (error) {
res.status(400).json({
error: 'Invalid product data'
});
}
});
app.put('/products/:id/stock', authenticateRequest, async (req, res) => {
const { quantity } = req.body;
try {
const product = await prisma.product.update({
where: { id: req.params.id },
data: {
stock: {
increment: quantity
}
}
});
res.json(product);
} catch (error) {
res.status(400).json({
error: 'Failed to update stock'
});
}
});
const PORT = process.env.PORT || 3002;
app.listen(PORT, () => {
console.log(`Products service running on port ${PORT}`);
});
Order Service 📦
// services/orders/src/server.ts
import express from 'express';
import { PrismaClient } from '@prisma/client';
import { authenticateRequest } from './middleware/auth';
import { publishEvent } from './utils/eventBus';
import { z } from 'zod';
const prisma = new PrismaClient();
const app = express();
app.use(express.json());
const orderItemSchema = z.object({
productId: z.string(),
quantity: z.number().int().positive()
});
const orderSchema = z.object({
items: z.array(orderItemSchema)
});
app.post('/orders', authenticateRequest, async (req, res) => {
const session = await prisma.$transaction(async (tx) => {
try {
const orderData = orderSchema.parse(req.body);
// Verify product availability
for (const item of orderData.items) {
const product = await tx.product.findUnique({
where: { id: item.productId }
});
if (!product || product.stock < item.quantity) {
throw new Error(`Insufficient stock for product ${item.productId}`);
}
// Update stock
await tx.product.update({
where: { id: item.productId },
data: {
stock: {
decrement: item.quantity
}
}
});
}
// Create order
const order = await tx.order.create({
data: {
userId: req.user.id,
items: {
create: orderData.items
},
status: 'PENDING'
},
include: {
items: true
}
});
// Publish event
await publishEvent('order.created', {
orderId: order.id,
userId: req.user.id,
items: order.items
});
return order;
} catch (error) {
throw error;
}
});
res.status(201).json(session);
});
app.get('/orders', authenticateRequest, async (req, res) => {
const orders = await prisma.order.findMany({
where: {
userId: req.user.id
},
include: {
items: {
include: {
product: true
}
}
}
});
res.json(orders);
});
app.patch('/orders/:id/status', authenticateRequest, async (req, res) => {
const { status } = req.body;
const order = await prisma.order.update({
where: { id: req.params.id },
data: { status },
include: {
items: true
}
});
await publishEvent('order.updated', {
orderId: order.id,
status,
items: order.items
});
res.json(order);
});
const PORT = process.env.PORT || 3003;
app.listen(PORT, () => {
console.log(`Orders service running on port ${PORT}`);
});
Kubernetes Deployment 🚢
# kubernetes/auth-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-deployment
spec:
replicas: 3
selector:
matchLabels:
app: auth
template:
metadata:
labels:
app: auth
spec:
containers:
- name: auth
image: microservices-demo/auth:latest
ports:
- containerPort: 3000
env:
- name: REDIS_URL
valueFrom:
configMapKeyRef:
name: app-config
key: redis-url
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: app-secrets
key: jwt-secret
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
spec:
selector:
app: auth
ports:
- port: 80
targetPort: 3000
type: ClusterIP
API Gateway 🌐
// gateway/src/server.ts
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import rateLimit from 'express-rate-limit';
import cors from 'cors';
const app = express();
app.use(cors());
app.use(express.json());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
app.use(limiter);
// Service routes
app.use('/auth', createProxyMiddleware({
target: 'http://auth-service',
changeOrigin: true,
pathRewrite: {
'^/auth': ''
}
}));
app.use('/users', createProxyMiddleware({
target: 'http://users-service',
changeOrigin: true,
pathRewrite: {
'^/users': ''
}
}));
app.use('/products', createProxyMiddleware({
target: 'http://products-service',
changeOrigin: true,
pathRewrite: {
'^/products': ''
}
}));
app.use('/orders', createProxyMiddleware({
target: 'http://orders-service',
changeOrigin: true,
pathRewrite: {
'^/orders': ''
}
}));
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => {
console.log(`API Gateway running on port ${PORT}`);
});
Event Bus 📡
// shared/eventBus.ts
import { Kafka } from 'kafkajs';
const kafka = new Kafka({
clientId: 'microservices-demo',
brokers: [process.env.KAFKA_BROKER!]
});
const producer = kafka.producer();
export async function publishEvent(
topic: string,
data: any
) {
await producer.connect();
try {
await producer.send({
topic,
messages: [
{
value: JSON.stringify(data)
}
]
});
} finally {
await producer.disconnect();
}
}
export async function subscribeToEvent(
topic: string,
handler: (data: any) => Promise<void>
) {
const consumer = kafka.consumer({
groupId: `${topic}-group`
});
await consumer.connect();
await consumer.subscribe({ topic });
await consumer.run({
eachMessage: async ({ message }) => {
const data = JSON.parse(message.value!.toString());
await handler(data);
}
});
}
Monitoring and Logging 📊
# kubernetes/monitoring.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: microservices-monitor
spec:
selector:
matchLabels:
app: microservices
endpoints:
- port: metrics
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: microservices-alerts
spec:
groups:
- name: microservices
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m])) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: High error rate detected
description: Error rate is above 10% for 5 minutes
Best Practices 📝
- Use Domain-Driven Design
- Implement Circuit Breakers
- Use Event Sourcing
- Implement proper logging
- Monitor service health
- Use proper authentication
- Implement rate limiting
- Use proper error handling
- Implement proper testing
- Use proper documentation
Additional Resources
Building microservices requires careful consideration of service boundaries, communication patterns, and deployment strategies. This guide provides a solid foundation for creating scalable and maintainable microservices using Node.js and Kubernetes.