Skip to content

0xYurii/Aegis

Repository files navigation

Aegis 🛡️

CI/CD Coverage Docker TypeScript

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.


Architecture

                        ┌─────────────────────────────────────────────────┐
                        │                  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)

Request Flow

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]

Circuit Breaker

Aegis implements a full three-state circuit breaker per downstream service instance to prevent cascading failures across the system.

When a downstream service starts failing, a naive gateway would keep forwarding requests to it — every one timing out, every user waiting, and the dying service getting hammered even harder under load. Aegis prevents this by tracking each service's health independently in an in-memory state registry (Map<string, CircuitState>). After 3 consecutive failures, the circuit opens and the load balancer stops routing to that instance entirely. After 60 seconds, the circuit enters a HALF_OPEN state and allows exactly one test request through. If it succeeds, the circuit resets to CLOSED. If it fails again, it slams back OPEN for another 60 seconds.

CLOSED ──(3 failures)──► OPEN ──(60s pass)──► HALF_OPEN
  ▲                                                │
  └──────(success)─────────────────────────────────┘
                          │
              (failure)───┘ → back to OPEN

Features

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

Getting Started

Prerequisites

  • Docker and Docker Compose installed
  • That's it — no Node.js needed locally

Installation

# 1. clone the repo
git clone https://github.com/0xYurii/Aegis
cd Aegis

# 2. create your .env file
cp .env.example .env

Open .env and 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 --build

That'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

Stopping

docker compose down

Plugin Config (per route)

// 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 },
    },
}

Testing

Run the test suite

npm install
npm test              # run all tests
npm run test:coverage # run with coverage report

100% statement/branch/function/line coverage across all plugins (auth, circuit-breaker, rate-limiter).

Generate a test JWT

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);

Test the gateway

# 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

Stats response example

[
  { "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" }
]

CI/CD

Every push runs the full test suite with coverage. On merge to main, Docker images for the gateway and both fake services are built and published to GitHub Container Registry automatically:

docker pull ghcr.io/0xyurii/aegis-gateway:latest
docker pull ghcr.io/0xyurii/aegis-user-service:latest
docker pull ghcr.io/0xyurii/aegis-product-service:latest

Pipeline: test → coverage → build → push — see .github/workflows/ci.yml


Stack

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)
Testing Jest + ts-jest, 100% coverage on plugins
Containers Docker + Docker Compose
CI/CD GitHub Actions → GitHub Container Registry

Built by Younes Hebaiche

About

A fully custom API Gateway built from scratch

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors