A production-grade API Gateway built from scratch Sits in front of your microservices and handles routing, auth, rate limiting, load balancing, and fault tolerance.
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β AEGIS GATEWAY :8000 β
β β
ββββββββββ β ββββββββββββ βββββββββββββ ββββββββββββββ β
β β Request β β β β Rate β β Load β β
β Client β βββββββββΊ β β Auth ββββΊβ Limiter ββββΊβ Balancer β β
β β β β Plugin β β (Redis) β β β β
ββββββββββ β ββββββββββββ βββββββββββββ βββββββ¬βββββββ β
β β β β
β 401 if round-robin pick β
β no token β β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββ
β
ββββββββββββββββββββββββββββββΌββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β user-service β β user-service β β user-service β
β -1 π’ β β -2 π’ β β -3 π΄ β
β CLOSED β β CLOSED β β OPEN β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
(skipped by
circuit breaker)
1. Request arrives at :8000
2. Logger generates X-Request-ID, starts timer
3. Auth Plugin validates JWT β 401 if missing or invalid
4. Rate Limiter checks Redis counter for this IP β 429 if exceeded
5. Load Balancer picks a healthy target (round-robin, skips OPEN circuits)
6. Gateway proxies request to target, forwards all headers + x-user-id
7. On success β recordSuccess() if HALF_OPEN test
8. On failure β recordFailure() β circuit breaker updates state
9. Logger prints: [METHOD] targetUrl β status (Xms) [request-id]
Aegis implements a full three-state circuit breaker per downstream service instance to prevent cascading failures across the system.
CLOSED ββ(3 failures)βββΊ OPEN ββ(60s pass)βββΊ HALF_OPEN
β² β
βββββββ(success)ββββββββββββββββββββββββββββββββββ
β
(failure)ββββ β back to OPEN
| Plugin | What it does |
|---|---|
| Auth | Validates JWT on protected routes, injects x-user-id header downstream |
| Rate Limiter | Redis-backed per-IP counter, configurable max requests + window |
| Load Balancer | Round-robin across service instances, skips unhealthy targets |
| Circuit Breaker | CLOSED β OPEN β HALF_OPEN state machine per instance |
| Logger | Logs method, target URL, status, duration, and correlation ID |
| Stats Endpoint | GET /gateway/stats returns live health snapshot of all instances |
- Docker and Docker Compose installed
- That's it β no Node.js needed locally
# 1. clone the repo
git clone https://github.com/0xYurii/Aegis
cd Aegis- Create
.envand fill in your values:
PORT=8000
REDIS_URL=redis://redis:6379
JWT_SECRET=your_secret_key_here# 3. build and start everything (gateway + redis + 6 fake services)
docker compose up --buildThat's it. All 9 containers spin up automatically:
β aegis-redis-1 Running
β aegis-user-service-1-1 Running β :3001
β aegis-user-service-2-1 Running β :3001
β aegis-user-service-3-1 Running β :3001
β aegis-product-service-1-1 Running β :3004
β aegis-product-service-2-1 Running β :3004
β aegis-product-service-3-1 Running β :3004
β aegis-gateway-1 Running β :8000
docker compose down// src/config/routes.config.ts
{
path: "/users",
target: [
"http://user-service-1:3001",
"http://user-service-2:3001",
"http://user-service-3:3001",
],
plugins: {
auth: true,
rateLimit: { max: 10, window: 60 },
},
}You need a token signed with your JWT_SECRET. Quick way β run this in Node:
const jwt = require("jsonwebtoken");
const token = jwt.sign({ userId: "test-123" }, "your_secret_key_here");
console.log(token);# no token β 401
curl http://localhost:8000/users
# with valid JWT β 200
curl -H "Authorization: Bearer <your_token>" http://localhost:8000/users
# another route
curl -H "Authorization: Bearer <your_token>" http://localhost:8000/product
# test rate limiting β fire 15 requests fast, 429 kicks in after 10
for i in {1..15}; do curl -H "Authorization: Bearer <your_token>" http://localhost:8000/users; done
# trigger circuit breaker β hit /fail 3 times to open the circuit
curl -H "Authorization: Bearer <your_token>" http://localhost:8000/users/fail
curl -H "Authorization: Bearer <your_token>" http://localhost:8000/users/fail
curl -H "Authorization: Bearer <your_token>" http://localhost:8000/users/fail
# check live health of all instances
curl http://localhost:8000/gateway/stats[
{ "path": "/users", "targetUrl": "http://user-service-1:3001", "failures": 3, "state": "OPEN" },
{ "path": "/users", "targetUrl": "http://user-service-2:3001", "failures": 0, "state": "CLOSED" },
{ "path": "/users", "targetUrl": "http://user-service-3:3001", "failures": 0, "state": "CLOSED" },
{ "path": "/product", "targetUrl": "http://product-service-1:3004", "failures": 0, "state": "CLOSED" },
{ "path": "/product", "targetUrl": "http://product-service-2:3004", "failures": 0, "state": "CLOSED" },
{ "path": "/product", "targetUrl": "http://product-service-3:3004", "failures": 0, "state": "CLOSED" }
]| Runtime | Node.js 20 + TypeScript |
| Framework | Express 5 |
| HTTP Client | Axios |
| Cache / State | Redis (ioredis) |
| Auth | JWT (jsonwebtoken) |
| Tracing | UUID correlation IDs (x-request-id) |
| Containers | Docker + Docker Compose |
Built by Younes Hebaiche