Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    manutej

    express-microservices-architecture

    manutej/express-microservices-architecture
    Coding
    38
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Complete guide for building scalable microservices with Express.js including middleware patterns, routing strategies, error handling, production architecture, and deployment best practices

    SKILL.md

    Express.js Microservices Architecture

    A comprehensive skill for building production-ready microservices with Express.js. Master middleware patterns, routing strategies, error handling, scalability techniques, and deployment architectures for Node.js microservices at scale.

    When to Use This Skill

    Use this skill when:

    • Building RESTful APIs and microservices with Node.js
    • Designing scalable distributed systems with Express.js
    • Implementing middleware-based architecture patterns
    • Creating API gateways and service mesh architectures
    • Developing production-ready Node.js applications
    • Migrating monoliths to microservices architecture
    • Building event-driven microservices
    • Implementing authentication, authorization, and security layers
    • Optimizing Express.js applications for high performance
    • Setting up monitoring, logging, and observability
    • Deploying Express.js apps with Docker and Kubernetes
    • Implementing circuit breakers and resilience patterns

    Core Concepts

    Express.js Fundamentals

    Express.js is a minimal and flexible Node.js web application framework that provides robust features for web and mobile applications. It's the de facto standard for building Node.js APIs and microservices.

    Key Characteristics:

    • Minimal: Unopinionated framework with essential web app features
    • Middleware-based: Request/response pipeline architecture
    • Routing: Powerful routing mechanism with parameter support
    • Template Engines: Support for various view engines
    • Performance: Built on top of Node.js for high performance
    • Extensible: Rich ecosystem of middleware and plugins

    Middleware Architecture

    Middleware functions are the backbone of Express.js applications. They have access to the request object (req), response object (res), and the next middleware function (next).

    Middleware Flow:

    Request → Middleware 1 → Middleware 2 → ... → Route Handler → Response
                    ↓              ↓                      ↓
               Error Handler  Error Handler         Error Handler
    

    Middleware Types:

    1. Application-level middleware: Bound to app instance
    2. Router-level middleware: Bound to express.Router() instance
    3. Error-handling middleware: Has 4 parameters (err, req, res, next)
    4. Built-in middleware: Express built-in functions (static, json, urlencoded)
    5. Third-party middleware: External packages (cors, helmet, morgan)

    Routing Strategies

    Express routing enables you to map HTTP methods and URLs to handler functions.

    Routing Components:

    • Route paths: String patterns, regex, or path parameters
    • Route parameters: Named URL segments (:userId)
    • Route handlers: Single or multiple callback functions
    • Response methods: res.send(), res.json(), res.status(), etc.
    • Router instances: Modular, mountable route handlers

    Error Handling

    Error handling in Express requires special middleware with 4 parameters: (err, req, res, next).

    Error Handling Flow:

    1. Synchronous errors are caught automatically
    2. Asynchronous errors must be passed to next(err)
    3. Error middleware processes errors centrally
    4. Proper status codes and error formats returned

    Microservices Principles

    Characteristics of Microservices:

    • Single Responsibility: Each service does one thing well
    • Independence: Services can be deployed independently
    • Decentralized: Each service owns its data
    • Resilience: Failure in one service doesn't crash entire system
    • Scalability: Scale services independently based on demand
    • Technology Diversity: Different services can use different tech stacks

    Microservices Patterns

    Pattern 1: API Gateway Pattern

    The API Gateway acts as a single entry point for all client requests, routing them to appropriate microservices.

    Benefits:

    • Single entry point for clients
    • Request routing and composition
    • Authentication and authorization
    • Rate limiting and throttling
    • Request/response transformation
    • Protocol translation

    Implementation Structure:

    Client → API Gateway → Microservice 1 (Users)
                        → Microservice 2 (Orders)
                        → Microservice 3 (Products)
                        → Microservice 4 (Notifications)
    

    Pattern 2: Service Discovery

    Services register themselves and discover other services dynamically.

    Approaches:

    • Client-side discovery: Client queries service registry
    • Server-side discovery: Load balancer queries registry
    • DNS-based discovery: Using DNS for service location

    Popular Tools:

    • Consul
    • Eureka
    • etcd
    • Kubernetes built-in discovery

    Pattern 3: Circuit Breaker

    Prevents cascading failures by stopping requests to failing services.

    States:

    • Closed: Normal operation, requests pass through
    • Open: Service failing, requests fail immediately
    • Half-Open: Testing if service recovered

    Pattern 4: Event-Driven Architecture

    Services communicate through events instead of direct calls.

    Components:

    • Event producers: Services that emit events
    • Event consumers: Services that listen to events
    • Message broker: RabbitMQ, Kafka, Redis
    • Event store: Persist events for replay

    Pattern 5: Database per Service

    Each microservice owns its database, ensuring loose coupling.

    Benefits:

    • Service independence
    • Technology diversity
    • Easier scaling
    • Clear boundaries

    Challenges:

    • Distributed transactions
    • Data consistency
    • Joins across services

    Pattern 6: Saga Pattern

    Manages distributed transactions across multiple services.

    Types:

    • Choreography: Services coordinate through events
    • Orchestration: Central coordinator manages transaction

    Pattern 7: CQRS (Command Query Responsibility Segregation)

    Separate read and write operations into different models.

    Benefits:

    • Optimized read/write models
    • Scalability
    • Performance
    • Flexibility

    Middleware Architecture Patterns

    Custom Middleware Development

    Middleware functions execute in the order they're defined.

    Basic Middleware Structure:

    const express = require('express');
    const app = express();
    
    // Basic middleware
    const requestLogger = (req, res, next) => {
      console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
      next(); // Pass control to next middleware
    };
    
    app.use(requestLogger);
    

    From Context7 - Saving Data in Request Object:

    const express = require('express');
    const app = express();
    const port = 3000;
    
    // Middleware to add user data to the request object
    const addUserInfo = (req, res, next) => {
      req.user = {
        id: 123,
        username: 'testuser'
      };
      next();
    };
    
    // Middleware to add request timestamp
    const addTimestamp = (req, res, next) => {
      req.requestTime = Date.now();
      next();
    };
    
    // Apply middleware globally
    app.use(addUserInfo);
    app.use(addTimestamp);
    
    app.get('/', (req, res) => {
      const userId = req.user.id;
      const username = req.user.username;
      const timestamp = req.requestTime;
    
      res.send(`User ID: ${userId}, Username: ${username}, Request Time: ${new Date(timestamp).toISOString()}`);
    });
    
    app.listen(port, () => {
      console.log(`Request data sharing example listening at http://localhost:${port}`);
    });
    

    Error-Handling Middleware

    Error middleware has 4 parameters and should be defined after all other middleware.

    From Context7 - Error Handling Middleware:

    const express = require('express');
    const app = express();
    const port = 3000;
    
    // A regular middleware
    app.use((req, res, next) => {
      console.log('Request received');
      next(); // Pass control to the next middleware
    });
    
    // A route that might throw an error
    app.get('/throw-error', (req, res, next) => {
      // Simulate an error
      const error = new Error('This is a simulated error');
      error.status = 400;
      next(error);
    });
    
    // Error-handling middleware (must have 4 arguments)
    app.use((err, req, res, next) => {
      console.error('Error caught:', err.message);
      res.status(err.status || 500).send(`An error occurred: ${err.message}`);
    });
    
    app.listen(port, () => {
      console.log(`Error middleware example listening at http://localhost:${port}`);
    });
    

    From Context7 - Global Error Handler:

    app.use(express.bodyParser())
    app.use(express.cookieParser())
    app.use(express.session())
    app.use(app.router) // the router itself (app.get(), app.put() etc)
    app.use(function(err, req, res, next){
      // if an error occurs Connect will pass it down
      // through these "error-handling" middleware
      // allowing you to respond however you like
      res.send(500, { error: 'Sorry something bad happened!' });
    })
    

    Route-Specific Middleware

    Apply middleware to specific routes for targeted functionality.

    From Context7 - Route Middleware:

    const express = require('express');
    const app = express();
    const port = 3000;
    
    // Middleware function
    const requestLogger = (req, res, next) => {
      console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
      next();
    };
    
    // Apply middleware to a specific route
    app.get('/protected', requestLogger, (req, res) => {
      res.send('This route is protected by middleware!');
    });
    
    // Apply middleware to multiple routes
    const adminMiddleware = (req, res, next) => {
      console.log('Admin access check...');
      // In a real app, you'd check user roles here
      next();
    };
    
    app.get('/admin/dashboard', adminMiddleware, (req, res) => {
      res.send('Welcome to the admin dashboard!');
    });
    
    // Middleware applied globally
    app.use(requestLogger);
    
    app.get('/', (req, res) => {
      res.send('Hello, world!');
    });
    
    app.listen(port, () => {
      console.log(`Route middleware example listening at http://localhost:${port}`);
    });
    

    Authentication Middleware

    const jwt = require('jsonwebtoken');
    
    // JWT 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, process.env.JWT_SECRET, (err, user) => {
        if (err) {
          return res.status(403).json({ error: 'Invalid or expired token' });
        }
        req.user = user;
        next();
      });
    };
    
    // Role-based Authorization Middleware
    const authorize = (...roles) => {
      return (req, res, next) => {
        if (!req.user) {
          return res.status(401).json({ error: 'Unauthorized' });
        }
    
        if (!roles.includes(req.user.role)) {
          return res.status(403).json({ error: 'Insufficient permissions' });
        }
    
        next();
      };
    };
    
    // Usage
    app.get('/admin/users', authenticateToken, authorize('admin'), (req, res) => {
      res.json({ users: [] });
    });
    

    Request Validation Middleware

    const { body, param, query, validationResult } = require('express-validator');
    
    // Validation middleware factory
    const validate = (validations) => {
      return async (req, res, next) => {
        await Promise.all(validations.map(validation => validation.run(req)));
    
        const errors = validationResult(req);
        if (errors.isEmpty()) {
          return next();
        }
    
        res.status(400).json({
          error: 'Validation failed',
          details: errors.array()
        });
      };
    };
    
    // Usage
    app.post('/users', validate([
      body('email').isEmail().normalizeEmail(),
      body('password').isLength({ min: 8 }),
      body('name').trim().notEmpty()
    ]), (req, res) => {
      // Request is validated
      res.json({ success: true });
    });
    

    Rate Limiting Middleware

    const rateLimit = require('express-rate-limit');
    const RedisStore = require('rate-limit-redis');
    const Redis = require('ioredis');
    
    // In-memory rate limiter
    const limiter = rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100, // Limit each IP to 100 requests per windowMs
      message: 'Too many requests, please try again later',
      standardHeaders: true,
      legacyHeaders: false,
    });
    
    // Redis-based rate limiter for distributed systems
    const redisClient = new Redis({
      host: process.env.REDIS_HOST,
      port: process.env.REDIS_PORT,
    });
    
    const distributedLimiter = rateLimit({
      store: new RedisStore({
        client: redisClient,
        prefix: 'rl:',
      }),
      windowMs: 15 * 60 * 1000,
      max: 100,
    });
    
    // Apply to all routes
    app.use('/api/', distributedLimiter);
    
    // Apply to specific routes
    app.post('/api/login', rateLimit({
      windowMs: 15 * 60 * 1000,
      max: 5, // Only 5 login attempts per 15 minutes
    }), loginHandler);
    

    Logging Middleware

    const morgan = require('morgan');
    const winston = require('winston');
    const { format } = winston;
    
    // Create Winston logger
    const logger = winston.createLogger({
      level: 'info',
      format: format.combine(
        format.timestamp(),
        format.errors({ stack: true }),
        format.json()
      ),
      defaultMeta: { service: 'user-service' },
      transports: [
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' }),
      ],
    });
    
    // Console logging in development
    if (process.env.NODE_ENV !== 'production') {
      logger.add(new winston.transports.Console({
        format: format.combine(
          format.colorize(),
          format.simple()
        )
      }));
    }
    
    // HTTP request logging with Morgan
    app.use(morgan('combined', {
      stream: {
        write: (message) => logger.info(message.trim())
      }
    }));
    
    // Custom logging middleware
    const requestLogger = (req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        const duration = Date.now() - start;
        logger.info({
          method: req.method,
          path: req.path,
          status: res.statusCode,
          duration: `${duration}ms`,
          ip: req.ip,
          userAgent: req.get('user-agent')
        });
      });
    
      next();
    };
    
    app.use(requestLogger);
    

    CORS Middleware

    const cors = require('cors');
    
    // Basic CORS
    app.use(cors());
    
    // Configured CORS
    const corsOptions = {
      origin: function (origin, callback) {
        const allowedOrigins = [
          'https://example.com',
          'https://app.example.com',
          process.env.FRONTEND_URL
        ];
    
        if (!origin || allowedOrigins.includes(origin)) {
          callback(null, true);
        } else {
          callback(new Error('Not allowed by CORS'));
        }
      },
      credentials: true,
      optionsSuccessStatus: 200,
      methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
      allowedHeaders: ['Content-Type', 'Authorization'],
      exposedHeaders: ['X-Total-Count', 'X-Page-Number'],
      maxAge: 86400, // 24 hours
    };
    
    app.use(cors(corsOptions));
    
    // CORS for specific routes
    app.options('/api/admin/*', cors(adminCorsOptions));
    app.use('/api/admin/', cors(adminCorsOptions));
    

    Security Middleware

    const helmet = require('helmet');
    const mongoSanitize = require('express-mongo-sanitize');
    const xss = require('xss-clean');
    const hpp = require('hpp');
    
    // Helmet - Set security headers
    app.use(helmet());
    
    // Custom security headers
    app.use((req, res, next) => {
      res.setHeader('X-Content-Type-Options', 'nosniff');
      res.setHeader('X-Frame-Options', 'DENY');
      res.setHeader('X-XSS-Protection', '1; mode=block');
      next();
    });
    
    // Sanitize data against NoSQL injection
    app.use(mongoSanitize());
    
    // Prevent XSS attacks
    app.use(xss());
    
    // Prevent HTTP Parameter Pollution
    app.use(hpp({
      whitelist: ['sort', 'fields', 'page', 'limit']
    }));
    
    // Content Security Policy
    app.use(helmet.contentSecurityPolicy({
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        scriptSrc: ["'self'"],
        imgSrc: ["'self'", 'data:', 'https:'],
      },
    }));
    

    Routing Strategies

    Basic Routing

    From Context7 - Express Routing:

    app.get('/', home);
    app.use('/public', require('st')(process.cwd()));
    app.get('/users', users.list);
    app.post('/users', users.create);
    

    Route Method Chaining

    From Context7 - Route Chaining:

    app.route('/users')
    .get(function(req, res, next) {
      // Get all users
      res.json({ users: [] });
    })
    .post(function(req, res, next) {
      // Create new user
      res.status(201).json({ user: {} });
    });
    

    Router Modules

    // routes/users.js
    const express = require('express');
    const router = express.Router();
    
    // Middleware specific to this router
    router.use((req, res, next) => {
      console.log('Time: ', Date.now());
      next();
    });
    
    // Define routes
    router.get('/', (req, res) => {
      res.json({ users: [] });
    });
    
    router.get('/:id', (req, res) => {
      res.json({ user: { id: req.params.id } });
    });
    
    router.post('/', (req, res) => {
      res.status(201).json({ user: req.body });
    });
    
    router.put('/:id', (req, res) => {
      res.json({ user: { id: req.params.id, ...req.body } });
    });
    
    router.delete('/:id', (req, res) => {
      res.status(204).send();
    });
    
    module.exports = router;
    
    // app.js
    const usersRouter = require('./routes/users');
    app.use('/api/users', usersRouter);
    

    Route Parameters

    // Named parameters
    app.get('/users/:userId/posts/:postId', (req, res) => {
      const { userId, postId } = req.params;
      res.json({ userId, postId });
    });
    
    // Parameter middleware
    app.param('userId', (req, res, next, userId) => {
      // Fetch user from database
      User.findById(userId)
        .then(user => {
          if (!user) {
            return res.status(404).json({ error: 'User not found' });
          }
          req.user = user;
          next();
        })
        .catch(next);
    });
    
    // Multiple callbacks
    app.param('postId', [
      validatePostId,
      fetchPost,
      checkPermissions
    ]);
    

    Query Parameters

    // GET /api/users?role=admin&active=true&page=2&limit=10
    app.get('/api/users', (req, res) => {
      const {
        role,
        active,
        page = 1,
        limit = 10,
        sort = '-createdAt'
      } = req.query;
    
      const query = {};
      if (role) query.role = role;
      if (active !== undefined) query.active = active === 'true';
    
      const skip = (page - 1) * limit;
    
      User.find(query)
        .sort(sort)
        .limit(parseInt(limit))
        .skip(skip)
        .then(users => res.json({ users, page, limit }))
        .catch(next);
    });
    

    API Versioning

    // Version 1 routes
    const v1Router = express.Router();
    v1Router.get('/users', (req, res) => {
      res.json({ version: 'v1', users: [] });
    });
    
    // Version 2 routes
    const v2Router = express.Router();
    v2Router.get('/users', (req, res) => {
      res.json({ version: 'v2', users: [], meta: {} });
    });
    
    // Mount versioned routes
    app.use('/api/v1', v1Router);
    app.use('/api/v2', v2Router);
    
    // Header-based versioning
    app.use('/api/users', (req, res, next) => {
      const version = req.headers['api-version'] || 'v1';
    
      if (version === 'v2') {
        return v2UsersHandler(req, res, next);
      }
      return v1UsersHandler(req, res, next);
    });
    

    RESTful Route Organization

    // controllers/users.controller.js
    class UsersController {
      async list(req, res, next) {
        try {
          const users = await User.find();
          res.json({ users });
        } catch (error) {
          next(error);
        }
      }
    
      async get(req, res, next) {
        try {
          res.json({ user: req.user });
        } catch (error) {
          next(error);
        }
      }
    
      async create(req, res, next) {
        try {
          const user = await User.create(req.body);
          res.status(201).json({ user });
        } catch (error) {
          next(error);
        }
      }
    
      async update(req, res, next) {
        try {
          const user = await User.findByIdAndUpdate(
            req.params.id,
            req.body,
            { new: true, runValidators: true }
          );
          res.json({ user });
        } catch (error) {
          next(error);
        }
      }
    
      async delete(req, res, next) {
        try {
          await User.findByIdAndDelete(req.params.id);
          res.status(204).send();
        } catch (error) {
          next(error);
        }
      }
    }
    
    // routes/users.routes.js
    const router = express.Router();
    const controller = new UsersController();
    
    router.get('/', controller.list);
    router.get('/:id', controller.get);
    router.post('/', controller.create);
    router.put('/:id', controller.update);
    router.delete('/:id', controller.delete);
    
    module.exports = router;
    

    Scalability Patterns

    Horizontal Scaling

    Deploy multiple instances of your service behind a load balancer.

    // Enable cluster mode
    const cluster = require('cluster');
    const os = require('os');
    
    if (cluster.isMaster) {
      const numCPUs = os.cpus().length;
    
      console.log(`Master process ${process.pid} is running`);
      console.log(`Forking ${numCPUs} workers...`);
    
      // Fork workers
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died. Restarting...`);
        cluster.fork();
      });
    } else {
      // Workers share the TCP connection
      const app = require('./app');
      const port = process.env.PORT || 3000;
    
      app.listen(port, () => {
        console.log(`Worker ${process.pid} started on port ${port}`);
      });
    }
    

    Load Balancing

    # nginx.conf
    upstream backend {
        least_conn;
        server localhost:3001;
        server localhost:3002;
        server localhost:3003;
        server localhost:3004;
    }
    
    server {
        listen 80;
    
        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
    

    Caching Strategies

    const Redis = require('ioredis');
    const redis = new Redis({
      host: process.env.REDIS_HOST,
      port: process.env.REDIS_PORT,
    });
    
    // Cache middleware
    const cacheMiddleware = (duration = 300) => {
      return async (req, res, next) => {
        if (req.method !== 'GET') {
          return next();
        }
    
        const key = `cache:${req.originalUrl}`;
    
        try {
          const cached = await redis.get(key);
    
          if (cached) {
            return res.json(JSON.parse(cached));
          }
    
          // Store original res.json
          const originalJson = res.json.bind(res);
    
          // Override res.json
          res.json = (body) => {
            redis.setex(key, duration, JSON.stringify(body));
            return originalJson(body);
          };
    
          next();
        } catch (error) {
          console.error('Cache error:', error);
          next();
        }
      };
    };
    
    // Usage
    app.get('/api/products', cacheMiddleware(600), async (req, res) => {
      const products = await Product.find();
      res.json({ products });
    });
    
    // Cache invalidation
    const invalidateCache = async (pattern) => {
      const keys = await redis.keys(pattern);
      if (keys.length > 0) {
        await redis.del(...keys);
      }
    };
    
    // Invalidate on updates
    app.post('/api/products', async (req, res) => {
      const product = await Product.create(req.body);
      await invalidateCache('cache:/api/products*');
      res.status(201).json({ product });
    });
    

    Database Connection Pooling

    const mongoose = require('mongoose');
    
    // MongoDB connection with pooling
    mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      poolSize: 10, // Maintain up to 10 socket connections
      socketTimeoutMS: 45000,
      family: 4,
    });
    
    // PostgreSQL with connection pooling
    const { Pool } = require('pg');
    const pool = new Pool({
      host: process.env.DB_HOST,
      port: process.env.DB_PORT,
      database: process.env.DB_NAME,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      max: 20, // Maximum number of clients
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    });
    
    // Query helper
    const query = async (text, params) => {
      const start = Date.now();
      const res = await pool.query(text, params);
      const duration = Date.now() - start;
      console.log('Executed query', { text, duration, rows: res.rowCount });
      return res;
    };
    
    module.exports = { query, pool };
    

    Response Compression

    const compression = require('compression');
    
    // Basic compression
    app.use(compression());
    
    // Custom compression settings
    app.use(compression({
      filter: (req, res) => {
        if (req.headers['x-no-compression']) {
          return false;
        }
        return compression.filter(req, res);
      },
      level: 6, // Compression level (0-9)
      threshold: 1024, // Minimum size to compress (bytes)
    }));
    

    Request Throttling

    const { Throttle } = require('stream-throttle');
    
    // Throttle large responses
    app.get('/api/large-dataset', (req, res) => {
      const dataStream = getLargeDataStream();
    
      // Throttle to 1MB/s
      const throttle = new Throttle({ rate: 1024 * 1024 });
    
      res.setHeader('Content-Type', 'application/json');
      dataStream.pipe(throttle).pipe(res);
    });
    

    Production Architecture

    Docker Containerization

    # Dockerfile
    FROM node:18-alpine AS builder
    
    WORKDIR /app
    
    # Copy package files
    COPY package*.json ./
    
    # Install dependencies
    RUN npm ci --only=production
    
    # Copy source code
    COPY . .
    
    # Production image
    FROM node:18-alpine
    
    WORKDIR /app
    
    # Create non-root user
    RUN addgroup -g 1001 -S nodejs && \
        adduser -S nodejs -u 1001
    
    # Copy from builder
    COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
    COPY --chown=nodejs:nodejs . .
    
    # Switch to non-root user
    USER nodejs
    
    # Expose port
    EXPOSE 3000
    
    # Health check
    HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
      CMD node healthcheck.js
    
    # Start application
    CMD ["node", "server.js"]
    
    # docker-compose.yml
    version: '3.8'
    
    services:
      api:
        build: .
        ports:
          - "3000:3000"
        environment:
          - NODE_ENV=production
          - REDIS_HOST=redis
          - MONGODB_URI=mongodb://mongo:27017/app
        depends_on:
          - redis
          - mongo
        restart: unless-stopped
        deploy:
          replicas: 3
          resources:
            limits:
              cpus: '0.5'
              memory: 512M
    
      redis:
        image: redis:7-alpine
        volumes:
          - redis-data:/data
        restart: unless-stopped
    
      mongo:
        image: mongo:6
        volumes:
          - mongo-data:/data/db
        restart: unless-stopped
    
      nginx:
        image: nginx:alpine
        ports:
          - "80:80"
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf
        depends_on:
          - api
        restart: unless-stopped
    
    volumes:
      redis-data:
      mongo-data:
    

    Process Management with PM2

    // ecosystem.config.js
    module.exports = {
      apps: [{
        name: 'api',
        script: './server.js',
        instances: 'max',
        exec_mode: 'cluster',
        autorestart: true,
        watch: false,
        max_memory_restart: '1G',
        env: {
          NODE_ENV: 'development',
          PORT: 3000
        },
        env_production: {
          NODE_ENV: 'production',
          PORT: 3000
        },
        error_file: './logs/err.log',
        out_file: './logs/out.log',
        log_file: './logs/combined.log',
        time: true,
        merge_logs: true,
      }]
    };
    

    Health Checks

    // healthcheck.js
    const http = require('http');
    
    const options = {
      host: 'localhost',
      port: process.env.PORT || 3000,
      path: '/health',
      timeout: 2000
    };
    
    const healthCheck = http.request(options, (res) => {
      console.log(`HEALTHCHECK STATUS: ${res.statusCode}`);
      if (res.statusCode === 200) {
        process.exit(0);
      } else {
        process.exit(1);
      }
    });
    
    healthCheck.on('error', (err) => {
      console.error('ERROR:', err);
      process.exit(1);
    });
    
    healthCheck.end();
    
    // Health endpoint
    app.get('/health', async (req, res) => {
      const health = {
        uptime: process.uptime(),
        message: 'OK',
        timestamp: Date.now()
      };
    
      try {
        // Check database connection
        await mongoose.connection.db.admin().ping();
        health.database = 'connected';
    
        // Check Redis connection
        await redis.ping();
        health.cache = 'connected';
    
        res.status(200).json(health);
      } catch (error) {
        health.message = error.message;
        res.status(503).json(health);
      }
    });
    
    // Readiness check
    app.get('/ready', (req, res) => {
      res.status(200).json({ ready: true });
    });
    
    // Liveness check
    app.get('/live', (req, res) => {
      res.status(200).json({ alive: true });
    });
    

    Graceful Shutdown

    // server.js
    const gracefulShutdown = () => {
      console.log('Received shutdown signal, closing server gracefully...');
    
      server.close(async () => {
        console.log('HTTP server closed');
    
        try {
          // Close database connections
          await mongoose.connection.close();
          console.log('MongoDB connection closed');
    
          // Close Redis connection
          await redis.quit();
          console.log('Redis connection closed');
    
          // Close other resources
          // ...
    
          console.log('Graceful shutdown completed');
          process.exit(0);
        } catch (err) {
          console.error('Error during shutdown:', err);
          process.exit(1);
        }
      });
    
      // Force shutdown after 10 seconds
      setTimeout(() => {
        console.error('Forcing shutdown after timeout');
        process.exit(1);
      }, 10000);
    };
    
    process.on('SIGTERM', gracefulShutdown);
    process.on('SIGINT', gracefulShutdown);
    

    Monitoring and Observability

    const promClient = require('prom-client');
    
    // Create a Registry
    const register = new promClient.Registry();
    
    // Add default metrics
    promClient.collectDefaultMetrics({ register });
    
    // Custom metrics
    const httpRequestDuration = new promClient.Histogram({
      name: 'http_request_duration_seconds',
      help: 'Duration of HTTP requests in seconds',
      labelNames: ['method', 'route', 'status_code'],
      buckets: [0.1, 0.5, 1, 2, 5]
    });
    
    const httpRequestTotal = new promClient.Counter({
      name: 'http_requests_total',
      help: 'Total number of HTTP requests',
      labelNames: ['method', 'route', 'status_code']
    });
    
    register.registerMetric(httpRequestDuration);
    register.registerMetric(httpRequestTotal);
    
    // Metrics middleware
    app.use((req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        const duration = (Date.now() - start) / 1000;
        const route = req.route ? req.route.path : req.path;
    
        httpRequestDuration.labels(req.method, route, res.statusCode).observe(duration);
        httpRequestTotal.labels(req.method, route, res.statusCode).inc();
      });
    
      next();
    });
    
    // Metrics endpoint
    app.get('/metrics', async (req, res) => {
      res.set('Content-Type', register.contentType);
      res.end(await register.metrics());
    });
    

    Distributed Tracing

    const { trace, context } = require('@opentelemetry/api');
    const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
    const { registerInstrumentations } = require('@opentelemetry/instrumentation');
    const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
    const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express');
    
    // Create tracer provider
    const provider = new NodeTracerProvider();
    provider.register();
    
    // Register instrumentations
    registerInstrumentations({
      instrumentations: [
        new HttpInstrumentation(),
        new ExpressInstrumentation(),
      ],
    });
    
    const tracer = trace.getTracer('user-service');
    
    // Custom tracing middleware
    const tracingMiddleware = (req, res, next) => {
      const span = tracer.startSpan(`HTTP ${req.method} ${req.path}`);
    
      span.setAttributes({
        'http.method': req.method,
        'http.url': req.url,
        'http.user_agent': req.get('user-agent'),
      });
    
      res.on('finish', () => {
        span.setAttributes({
          'http.status_code': res.statusCode,
        });
        span.end();
      });
    
      next();
    };
    
    app.use(tracingMiddleware);
    

    Best Practices

    Project Structure

    express-microservice/
    ├── src/
    │   ├── config/
    │   │   ├── database.js
    │   │   ├── redis.js
    │   │   └── logger.js
    │   ├── controllers/
    │   │   ├── users.controller.js
    │   │   └── auth.controller.js
    │   ├── middleware/
    │   │   ├── auth.js
    │   │   ├── validation.js
    │   │   ├── errorHandler.js
    │   │   └── requestLogger.js
    │   ├── models/
    │   │   └── user.model.js
    │   ├── routes/
    │   │   ├── index.js
    │   │   ├── users.routes.js
    │   │   └── auth.routes.js
    │   ├── services/
    │   │   ├── users.service.js
    │   │   ├── auth.service.js
    │   │   └── email.service.js
    │   ├── utils/
    │   │   ├── apiError.js
    │   │   ├── catchAsync.js
    │   │   └── validators.js
    │   ├── app.js
    │   └── server.js
    ├── tests/
    │   ├── unit/
    │   ├── integration/
    │   └── e2e/
    ├── .env
    ├── .env.example
    ├── .dockerignore
    ├── Dockerfile
    ├── docker-compose.yml
    ├── ecosystem.config.js
    ├── package.json
    └── README.md
    

    Environment Configuration

    // config/env.js
    const dotenv = require('dotenv');
    const Joi = require('joi');
    
    dotenv.config();
    
    const envSchema = Joi.object({
      NODE_ENV: Joi.string()
        .valid('development', 'production', 'test')
        .default('development'),
      PORT: Joi.number().default(3000),
      MONGODB_URI: Joi.string().required(),
      REDIS_HOST: Joi.string().required(),
      REDIS_PORT: Joi.number().default(6379),
      JWT_SECRET: Joi.string().required(),
      JWT_EXPIRES_IN: Joi.string().default('7d'),
      LOG_LEVEL: Joi.string()
        .valid('error', 'warn', 'info', 'debug')
        .default('info'),
    }).unknown();
    
    const { value: env, error } = envSchema.validate(process.env);
    
    if (error) {
      throw new Error(`Config validation error: ${error.message}`);
    }
    
    module.exports = {
      env: env.NODE_ENV,
      port: env.PORT,
      mongodb: {
        uri: env.MONGODB_URI,
      },
      redis: {
        host: env.REDIS_HOST,
        port: env.REDIS_PORT,
      },
      jwt: {
        secret: env.JWT_SECRET,
        expiresIn: env.JWT_EXPIRES_IN,
      },
      logging: {
        level: env.LOG_LEVEL,
      },
    };
    

    Error Handling Best Practices

    // utils/apiError.js
    class ApiError extends Error {
      constructor(statusCode, message, isOperational = true, stack = '') {
        super(message);
        this.statusCode = statusCode;
        this.isOperational = isOperational;
        if (stack) {
          this.stack = stack;
        } else {
          Error.captureStackTrace(this, this.constructor);
        }
      }
    }
    
    module.exports = ApiError;
    
    // utils/catchAsync.js
    const catchAsync = (fn) => {
      return (req, res, next) => {
        Promise.resolve(fn(req, res, next)).catch(next);
      };
    };
    
    module.exports = catchAsync;
    
    // middleware/errorHandler.js
    const config = require('../config/env');
    const logger = require('../config/logger');
    const ApiError = require('../utils/apiError');
    
    const errorConverter = (err, req, res, next) => {
      let error = err;
      if (!(error instanceof ApiError)) {
        const statusCode = error.statusCode || 500;
        const message = error.message || 'Internal Server Error';
        error = new ApiError(statusCode, message, false, err.stack);
      }
      next(error);
    };
    
    const errorHandler = (err, req, res, next) => {
      let { statusCode, message } = err;
    
      if (config.env === 'production' && !err.isOperational) {
        statusCode = 500;
        message = 'Internal Server Error';
      }
    
      res.locals.errorMessage = err.message;
    
      const response = {
        code: statusCode,
        message,
        ...(config.env === 'development' && { stack: err.stack }),
      };
    
      if (config.env === 'development') {
        logger.error(err);
      }
    
      res.status(statusCode).json(response);
    };
    
    module.exports = {
      errorConverter,
      errorHandler,
    };
    

    Testing Strategies

    // tests/integration/users.test.js
    const request = require('supertest');
    const app = require('../../src/app');
    const { User } = require('../../src/models');
    
    describe('User API', () => {
      beforeEach(async () => {
        await User.deleteMany({});
      });
    
      describe('POST /api/users', () => {
        it('should create a new user', async () => {
          const userData = {
            name: 'John Doe',
            email: 'john@example.com',
            password: 'password123',
          };
    
          const res = await request(app)
            .post('/api/users')
            .send(userData)
            .expect(201);
    
          expect(res.body).toHaveProperty('user');
          expect(res.body.user).toHaveProperty('id');
          expect(res.body.user.email).toBe(userData.email);
          expect(res.body.user).not.toHaveProperty('password');
        });
    
        it('should return 400 for invalid email', async () => {
          const userData = {
            name: 'John Doe',
            email: 'invalid-email',
            password: 'password123',
          };
    
          const res = await request(app)
            .post('/api/users')
            .send(userData)
            .expect(400);
    
          expect(res.body).toHaveProperty('error');
        });
      });
    
      describe('GET /api/users/:id', () => {
        it('should return user by id', async () => {
          const user = await User.create({
            name: 'John Doe',
            email: 'john@example.com',
            password: 'hashedpassword',
          });
    
          const res = await request(app)
            .get(`/api/users/${user.id}`)
            .expect(200);
    
          expect(res.body.user.id).toBe(user.id);
        });
    
        it('should return 404 for non-existent user', async () => {
          await request(app)
            .get('/api/users/507f1f77bcf86cd799439011')
            .expect(404);
        });
      });
    });
    

    Performance Optimization

    // Enable gzip compression
    const compression = require('compression');
    app.use(compression());
    
    // Use efficient JSON parsing
    app.use(express.json({ limit: '10mb' }));
    
    // Database query optimization
    const getUsers = async (filters) => {
      return User.find(filters)
        .select('name email role') // Select only needed fields
        .lean() // Return plain objects instead of Mongoose documents
        .limit(100);
    };
    
    // Implement pagination
    const paginateResults = async (model, page = 1, limit = 10) => {
      const skip = (page - 1) * limit;
    
      const [results, total] = await Promise.all([
        model.find().skip(skip).limit(limit).lean(),
        model.countDocuments(),
      ]);
    
      return {
        results,
        pagination: {
          page,
          limit,
          total,
          pages: Math.ceil(total / limit),
        },
      };
    };
    
    // Use indexes
    userSchema.index({ email: 1 });
    userSchema.index({ role: 1, createdAt: -1 });
    
    // Connection pooling and keep-alive
    const agent = new http.Agent({
      keepAlive: true,
      maxSockets: 50,
    });
    

    Security Best Practices

    // Validate and sanitize inputs
    const { body } = require('express-validator');
    
    const userValidation = [
      body('email').isEmail().normalizeEmail(),
      body('password').isLength({ min: 8 }).trim(),
      body('name').trim().escape(),
    ];
    
    // Implement rate limiting
    const loginLimiter = rateLimit({
      windowMs: 15 * 60 * 1000,
      max: 5,
      skipSuccessfulRequests: true,
    });
    
    app.post('/api/auth/login', loginLimiter, loginHandler);
    
    // Use parameterized queries
    const getUserByEmail = async (email) => {
      return User.findOne({ email }); // Protected against NoSQL injection
    };
    
    // Implement CSRF protection
    const csrf = require('csurf');
    app.use(csrf({ cookie: true }));
    
    // Set secure cookies
    res.cookie('token', token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    });
    
    // Hash passwords
    const bcrypt = require('bcrypt');
    const hashPassword = async (password) => {
      return bcrypt.hash(password, 12);
    };
    

    Examples

    Example 1: Basic Express Microservice

    const express = require('express');
    const helmet = require('helmet');
    const cors = require('cors');
    const morgan = require('morgan');
    
    const app = express();
    
    // Middleware
    app.use(helmet());
    app.use(cors());
    app.use(morgan('combined'));
    app.use(express.json());
    
    // Routes
    app.get('/health', (req, res) => {
      res.json({ status: 'healthy' });
    });
    
    app.get('/api/users', (req, res) => {
      res.json({ users: [] });
    });
    
    // Error handling
    app.use((err, req, res, next) => {
      console.error(err.stack);
      res.status(500).json({ error: 'Something went wrong!' });
    });
    
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });
    

    Example 2: Authentication Service

    const express = require('express');
    const jwt = require('jsonwebtoken');
    const bcrypt = require('bcrypt');
    const { body, validationResult } = require('express-validator');
    
    const router = express.Router();
    
    // Register
    router.post('/register',
      body('email').isEmail(),
      body('password').isLength({ min: 8 }),
      async (req, res, next) => {
        try {
          const errors = validationResult(req);
          if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
          }
    
          const { email, password } = req.body;
    
          // Check if user exists
          const existingUser = await User.findOne({ email });
          if (existingUser) {
            return res.status(409).json({ error: 'User already exists' });
          }
    
          // Hash password
          const hashedPassword = await bcrypt.hash(password, 12);
    
          // Create user
          const user = await User.create({
            email,
            password: hashedPassword,
          });
    
          // Generate token
          const token = jwt.sign(
            { userId: user.id },
            process.env.JWT_SECRET,
            { expiresIn: '7d' }
          );
    
          res.status(201).json({ token, user: { id: user.id, email: user.email } });
        } catch (error) {
          next(error);
        }
      }
    );
    
    // Login
    router.post('/login',
      body('email').isEmail(),
      body('password').exists(),
      async (req, res, next) => {
        try {
          const errors = validationResult(req);
          if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
          }
    
          const { email, password } = req.body;
    
          // Find user
          const user = await User.findOne({ email }).select('+password');
          if (!user) {
            return res.status(401).json({ error: 'Invalid credentials' });
          }
    
          // Check password
          const isValidPassword = await bcrypt.compare(password, user.password);
          if (!isValidPassword) {
            return res.status(401).json({ error: 'Invalid credentials' });
          }
    
          // Generate token
          const token = jwt.sign(
            { userId: user.id },
            process.env.JWT_SECRET,
            { expiresIn: '7d' }
          );
    
          res.json({ token, user: { id: user.id, email: user.email } });
        } catch (error) {
          next(error);
        }
      }
    );
    
    module.exports = router;
    

    Example 3: API Gateway Pattern

    const express = require('express');
    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    const app = express();
    
    // Service discovery (simplified)
    const services = {
      users: 'http://users-service:3001',
      products: 'http://products-service:3002',
      orders: 'http://orders-service:3003',
    };
    
    // Authentication middleware
    const authenticate = (req, res, next) => {
      const token = req.headers.authorization?.split(' ')[1];
    
      if (!token) {
        return res.status(401).json({ error: 'No token provided' });
      }
    
      try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded;
        next();
      } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
      }
    };
    
    // Rate limiting
    const limiter = rateLimit({
      windowMs: 15 * 60 * 1000,
      max: 100,
    });
    
    app.use(limiter);
    
    // Proxy routes
    app.use('/api/users', authenticate, createProxyMiddleware({
      target: services.users,
      changeOrigin: true,
      pathRewrite: { '^/api/users': '' },
    }));
    
    app.use('/api/products', createProxyMiddleware({
      target: services.products,
      changeOrigin: true,
      pathRewrite: { '^/api/products': '' },
    }));
    
    app.use('/api/orders', authenticate, createProxyMiddleware({
      target: services.orders,
      changeOrigin: true,
      pathRewrite: { '^/api/orders': '' },
    }));
    
    // Error handling
    app.use((err, req, res, next) => {
      console.error(err);
      res.status(500).json({ error: 'Gateway error' });
    });
    
    const PORT = process.env.PORT || 8000;
    app.listen(PORT, () => {
      console.log(`API Gateway running on port ${PORT}`);
    });
    

    See EXAMPLES.md for 15+ additional comprehensive examples including circuit breakers, event-driven patterns, service mesh integration, and more.


    Skill Version: 1.0.0 Last Updated: October 2025 Skill Category: Microservices, Backend Development, Node.js, Production Architecture Compatible With: Express.js 4.x/5.x, Node.js 16+, Docker, Kubernetes

    Recommended Servers
    Vercel Grep
    Vercel Grep
    Repository
    manutej/luxor-claude-marketplace
    Files