All posts/
Node.jsDockerMicroservices

Building Scalable Microservices with Node.js and Docker

Feb 20, 2026·8 min read

A deep-dive into decomposing monoliths into services, containerising them with Docker, and wiring everything together with an API gateway and service discovery.

What Are Microservices?

Microservices is an architectural style that structures an application as a collection of small, independently deployable services. Each service owns its data, communicates over well-defined APIs, and can be scaled, deployed, and restarted without affecting the rest of the system.

Creating Your First Service

Start with a minimal Express app that serves a single responsibility. Keep it stateless, expose a health-check endpoint, and drive all configuration through environment variables.

javascript
const express = require('express');
const app = express();
app.use(express.json());

app.get('/health', (_req, res) => res.json({ status: 'ok' }));

app.get('/users/:id', async (req, res) => {
  const user = await db.findUser(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json(user);
});

app.listen(process.env.PORT ?? 3001, () =>
  console.log('user-service ready'),
);

Containerising with Docker

Use multi-stage builds so the final image contains only the production artifact — no dev dependencies, no build tooling.

dockerfile
# ---- build ----
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ---- run ----
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
EXPOSE 3001
CMD ["node", "dist/server.js"]

Service Communication Patterns

  • Synchronous REST — simple but creates runtime coupling between services
  • Async messaging via RabbitMQ / Kafka — decouples producers and consumers
  • gRPC — low-latency binary protocol ideal for internal service calls
  • GraphQL Federation — compose multiple service graphs into one public API

API Gateway

An API gateway is the single entry point for all clients. It handles routing, rate limiting, authentication, and request aggregation so each service doesn't need to reimplement these cross-cutting concerns.

Design each service to do one thing and do it well. When it starts doing two things, split it.
NextMastering TypeScript Generics: From Basics to Advanced Patterns