Skip to content

Repository files navigation

🚀 PulseGateway

A production-style API Gateway built with Node.js and Express, demonstrating core distributed systems patterns: reverse proxying, JWT authentication, request validation, rate limiting, Redis caching, round-robin load balancing, retry logic, health checks, and observability — all fronting a set of dummy backend microservices.

Built as a hands-on systems design project to go deep on the concepts that come up in backend/infrastructure interviews, rather than another CRUD tutorial project.


Why this exists

Most backend portfolio projects are CRUD apps with a database. This one is different: it's infrastructure. It sits in front of other services and solves problems every real distributed system has to solve — how do you authenticate once instead of in every service? How do you protect backends from being overwhelmed? How do you keep serving traffic when one instance goes down? How do you even know if your system is healthy?

PulseGateway answers each of these with a real, working implementation — not a diagram, actual code you can run.


Architecture

                        ┌─────────────────────────────┐
                        │           CLIENT             │
                        └──────────────┬───────────────┘
                                       │ HTTP request
                                       ▼
                        ┌─────────────────────────────┐
                        │        PULSEGATEWAY          │
                        │                              │
                        │  1. Metrics (timing starts)  │
                        │  2. Auth Middleware (JWT)    │
                        │  3. Rate Limiter (Redis)     │
                        │  4. Cache Check (Redis)      │
                        │       │ cache miss           │
                        │       ▼                      │
                        │  5. Load Balancer (RR)       │
                        │  6. Retry Layer (on failure)  │
                        │  7. Proxy Router              │
                        │  8. Health Checker (bg loop)  │
                        └──────┬───────────────┬────────┘
                               │               │
                     ┌─────────▼──────┐ ┌───────▼─────────┐
                     │  User Service   │ │ Product Service │
                     │  (2 instances)  │ │  (1 instance)   │
                     └─────────────────┘ └─────────────────┘

Request flow: Client → Metrics timer starts → JWT verified → Rate limit checked (per-user via Redis) → Cache checked (Redis) → on miss: Load balancer picks a healthy backend instance (round-robin) → Proxy forwards the request → on failure: automatic retry against a different instance (GET only) → response cached and returned → Metrics recorded on completion.

Background process: A health checker pings every backend instance every 10 seconds, marking instances healthy/unhealthy so the load balancer never routes to a known-dead instance.


Features

Feature What it does
Reverse Proxy Routes /api/users and /api/products to their respective backend services
JWT Authentication Verifies a signed token on every request before it reaches any backend
Zod Validation Schema-validates request bodies, rejecting malformed data before it wastes a backend call
Rate Limiting Redis-backed, per-authenticated-user, fixed-window (10 req/60s), fails open if Redis is down
Redis Caching Caches successful GET responses for 30s, cutting backend load and latency dramatically
Round-Robin Load Balancing Distributes traffic across multiple instances of the same service
Retry Logic Automatically retries failed GET requests against a different healthy instance
Health Checks Background polling marks unreachable instances unhealthy so they're skipped by the load balancer
Metrics Endpoint /metrics exposes request counts, avg response time, cache hit ratio, status code breakdown
Live Dashboard /dashboard.html — auto-refreshing visual view of gateway performance
Dockerized Full system (gateway + 2 backend service instances + product service + Redis) runs with one command

Tech Stack

  • Runtime: Node.js, Express
  • Proxying: http-proxy-middleware
  • Caching / Rate Limiting: Redis (ioredis)
  • Auth: jsonwebtoken
  • Validation: Zod
  • Retry HTTP calls: axios
  • Containerization: Docker, Docker Compose

Getting Started

Option A — Docker (recommended, one command)

docker compose up --build

This starts Redis, the gateway, both User Service instances, and the Product Service together, wired via Docker's internal networking.

Option B — Run locally without Docker

Requires Redis running locally (redis-server or Docker: docker run -d -p 6379:6379 redis:7-alpine).

npm install

# In separate terminals:
npm run user-service        # port 4001
npm run user-service-2      # port 4003
npm run product-service     # port 4002
npm start                   # gateway, port 3000

Generate a test JWT

node src/utils/generateToken.js

Try it

curl http://localhost:3000/api/users -H "Authorization: Bearer <token>"
curl http://localhost:3000/metrics

Then open http://localhost:3000/dashboard.html in a browser to see live metrics.


Project Structure

pulsegateway/
├── src/
│   ├── server.js                 # entry point, middleware chain assembly
│   ├── config/
│   │   ├── services.js           # backend service registry
│   │   └── redisClient.js
│   ├── middleware/
│   │   ├── auth.js               # JWT verification
│   │   ├── validate.js           # Zod schema validation
│   │   ├── rateLimiter.js        # Redis-backed rate limiting
│   │   └── metrics.js            # request metrics collection
│   ├── gateway/
│   │   ├── proxyRouter.js        # reverse proxy, caching, retry logic
│   │   ├── loadBalancer.js       # round-robin instance selection
│   │   └── healthChecker.js      # background instance health polling
│   ├── routes/
│   │   └── metricsRoute.js       # /metrics and /health endpoints
│   ├── schemas/
│   │   └── userSchemas.js
│   └── utils/
│       └── generateToken.js      # dev helper to mint test JWTs
├── services/                     # dummy backend microservices
│   ├── user-service/
│   ├── user-service-2/
│   └── product-service/
├── public/
│   └── dashboard.html            # live metrics dashboard
├── Dockerfile
├── docker-compose.yml
└── .env

Known Limitations (intentional scope decisions)

  • Metrics are stored in-memory and reset on restart — in production this would be Prometheus + Grafana.
  • Rate limiting uses a fixed window, not sliding window or token bucket — a deliberate simplicity tradeoff.
  • Retry logic only handles GET requests, by design — retrying non-idempotent methods risks duplicate side effects.
  • depends_on in Docker Compose controls start order, not readiness — mitigated by the app's fail-open Redis handling.

These aren't oversights — they're documented, deliberate scope decisions appropriate for a focused portfolio project, with the production-grade alternative noted for each.


Author

Built by Ruturaj Pawar as a systems-design-focused portfolio project.

About

Production-style API Gateway built with Node.js & Express JWT auth, Redis caching & rate limiting, round-robin load balancing, health checks, retry logic, and a live metrics dashboard. Fully Dockerized with Docker Compose for one-command deployment across a 5-service architecture.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages