Stack: MongoDB · Express · React · Node.js · Redis · BullMQ · Socket.io
The NexGate control plane and dashboard are live. You can log in and view the live metrics, manage API keys, and configure rate limits in real-time.
┌──────────────────────────────────────────────────────────────────┐
│ DATA PLANE │
│ Consumer → Express Gateway → Redis (Rate Limit) → Upstream API │
│ ↓ (async, post-response) │
│ BullMQ Log Queue (Redis-backed) │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE │
│ Log Consumer Worker → MongoDB (request_logs time-series) │
│ Cost Aggregator (nightly cron) → MongoDB (cost_reports) │
│ SLA Monitor (5-min cron) → MongoDB (sla_definitions) │
│ Socket.io (Redis pub/sub adapter) → React Dashboard │
│ Management API (Express) → React Portal │
└──────────────────────────────────────────────────────────────────┘
| Process | Role | Writes to DB? | Reads Redis? |
|---|---|---|---|
gateway |
Proxy + Auth + Rate Limit | ❌ Never directly | ✅ Rate limits, cache |
api |
Management REST + Socket.io | ✅ Via Mongoose | ✅ Pub/sub adapter |
workers |
Log consumer, Cost, SLA | ✅ MongoDB only | ✅ BullMQ queues |
nexgate/
├── packages/
│ ├── gateway/ # Express API Gateway (Data Plane)
│ │ └── src/
│ │ ├── algorithms/ # slidingWindowLog, tokenBucket, fixedWindow
│ │ ├── db/ # MongoDB + Redis connections, all 8 models
│ │ ├── middleware/ # auth, rateLimit, scope, proxy, metrics
│ │ ├── queues/ # BullMQ log queue producer
│ │ └── services/ # cache, circuitBreaker
│ ├── api/ # Management API + Socket.io (Control Plane)
│ │ └── src/
│ │ ├── routes/ # teams, apis, keys, sla, costs, alerts, analytics
│ │ └── socket/ # Socket.io + Redis adapter + metrics aggregation
│ ├── workers/ # BullMQ Workers
│ │ └── src/workers/
│ │ ├── logConsumer.js # Batched MongoDB writes from queue
│ │ ├── costAggregator.js # Nightly cost attribution with dist. lock
│ │ └── slaMonitor.js # 5-min P95 evaluation + alert deduplication
│ └── portal/ # React Dashboard (Vite + Tailwind + Recharts)
│ └── src/
│ ├── components/ # Dashboard, APIs, Keys, SLA, Costs, Alerts
│ ├── hooks/ # useSocket (Socket.io client)
│ └── store/ # Zustand (auth, live metrics, UI)
├── scripts/
│ ├── init-db.js # Creates all collections + indexes (run before first start)
│ └── seed.js # Demo data: 3 teams, 6 APIs, keys, rules
├── .github/workflows/
│ └── ci-cd.yml # GitHub Actions: lint → test → build → deploy
└── docker-compose.yml # Local MongoDB + Redis (no cloud account needed)
| Algorithm | Use Case | Redis Memory | Precision |
|---|---|---|---|
| Sliding Window Log | Financial APIs, high-security | High — O(maxRequests) per key | Exact |
| Token Bucket | Batch APIs, burst-tolerant | O(1) per key (~16 bytes) | Exact |
| Fixed Window | High-volume reads, analytics | O(1) per key (~8 bytes) | ±2× at boundaries |
- Keys are 256-bit random tokens (not passwords) → brute force requires 2²⁵⁶ attempts
- SHA-256: ~1µs per hash → negligible on hot path; deterministic → O(1) lookup
- bcrypt at cost=12: ~100ms per hash → 100,000× slower → unacceptable for gateway auth
- Prefix
nxg_enables secret scanning and log triage (same pattern as GitHub, Stripe)
- P99 MongoDB write: ~200ms under load. BullMQ.add() (Redis XADD): ~0.5ms P99
- Gateway response latency is completely decoupled from log write latency
- Workers absorb MongoDB slowdowns; queue depth increases, response time doesn't
- Node.js 18+
- Docker Desktop
git clone <repo-url> nexgate
cd nexgate
npm installdocker compose up -d
# MongoDB: mongodb://nexgate:nexgate_dev_password@localhost:27017/nexgate?authSource=admin
# Redis: redis://localhost:6379# Gateway
cp packages/gateway/.env.example packages/gateway/.env
# Edit: MONGODB_URI and REDIS_URL (use docker-compose values above)
# Management API
cp packages/api/.env.example packages/api/.env
# Edit: same MongoDB + Redis, plus JWT_SECRET (any random 32-char string)
# Portal
echo "VITE_API_URL=http://localhost:4000/api" > packages/portal/.envnode scripts/init-db.jsnode scripts/seed.js
# ⚠ COPY the printed API keys — they won't be shown againnpm run dev
# Gateway: http://localhost:3000
# Mgmt API: http://localhost:4000
# Portal: http://localhost:5173
# Login: admin@nexgate.io / Admin123!# Test the gateway with a seeded API key
# Replace <your-nxg-key> with the key printed by seed.js
# Replace <api-id> with an API ObjectId from the portal
# Test Fixed Window (should allow first 5, deny 6th)
for i in {1..7}; do
curl -s -o /dev/null -w "Request $i: HTTP %{http_code}\n" \
-H "x-nexgate-key: <your-nxg-key>" \
http://localhost:3000/proxy/<api-id>/health
done
# Run SLA monitor now (don't wait 5 minutes)
node packages/workers/src/index.js --run-sla-now
# Run cost aggregation now
node packages/workers/src/index.js --run-cost-now| Service | Platform | Tier |
|---|---|---|
| React Portal | Vercel | Free (Hobby) |
| Express Gateway | Render | Free Web Service |
| Management API | Render | Free Web Service |
| BullMQ Workers | Render | Free Background Worker |
| MongoDB | Atlas | M0 Free (512 MB) |
| Redis | Upstash | Free (10K commands/day) |
Upstash free tier: 10,000 commands/day
Rate limiting uses 2 Redis commands per request (Lua script counts as 1):
- 10,000 ÷ 2 = 5,000 proxied requests/day before hitting the limit
For production beyond 5K req/day: upgrade to Upstash Pay-As-You-Go (~$0.20 per 100K commands).
Render free tier spins down after 15 minutes of inactivity. Cold start adds 10-30 seconds to the first request.
Mitigation options (cheapest → most robust):
-
External ping (free): Use UptimeRobot (free plan, 5-minute interval) to ping
GET /healthevery 5 minutes. Prevents spindown entirely. -
Self-ping (free, architectural): Add a
setIntervalin the gateway that calls its own/healthendpoint every 10 minutes. Works without external services. -
Render paid tier ($7/month): No spindown. Dedicated CPU. Recommended for production traffic.
M0 storage limit: 512 MB
At 1000 RPS with 30-day TTL on request_logs:
- Each log document: ~500 bytes
- 1000 req/s × 86,400 s/day × 500 bytes = 43 GB/day ← exceeds M0!
- Practical M0 limit: ~500 requests/day sustained, OR reduce TTL to 1 day
For production volume: MongoDB Atlas M2 ($9/month) → 2GB, M5 ($25/month) → 5GB.
| Collection | Type | TTL | Write Volume |
|---|---|---|---|
teams |
Regular | None | Very low |
apis |
Regular | None | Low |
apikeys |
Regular | On expiresAt |
Low |
ratelimitrules |
Regular | None | Very low |
requestlogs |
Time-Series | 30 days | Very high |
sladefinitions |
Regular | None | Low |
costmodels |
Regular | None | Very low |
costreports |
Regular | None | Low |
alerts |
Regular | 90 days | Medium |
- API keys hashed with SHA-256 (never stored in plaintext)
- Raw key shown exactly once, never stored
- JWT with 1-day expiry + 7-day refresh token
- Rate limiting on management API (500/15min global, 20/15min for login)
- Helmet.js security headers on management API
- CORS configured to allow only portal origin
- Circuit breaker prevents cascading failures
- Scope-based access control (read/write/admin)
- API key header stripped before proxying to upstream
- MongoDB connection uses TLS (Atlas default)
- Redis TLS for Upstash connections
- Environment variable validation at startup (Zod)
- Secrets in GitHub Secrets / Render environment (never in code)
| Event | Direction | Payload | Frequency |
|---|---|---|---|
metrics:api |
Server → Client | { apiId, rps, p95LatencyMs, errorRatePct } |
1/sec |
metrics:team |
Server → Client | Team aggregate | 1/sec |
metrics:platform |
Server → Client (admin) | Platform aggregate | 1/sec |
subscribe:api |
Client → Server | apiId |
On demand |
alert:new |
Server → Client | Alert document | On alert |
| Variable | Required | Default | Description |
|---|---|---|---|
MONGODB_URI |
✅ | — | Atlas connection string |
REDIS_URL |
✅ | — | Redis connection URL |
REDIS_TLS |
false |
Enable TLS (set true for Upstash) |
|
GATEWAY_PORT |
3000 |
Gateway HTTP port | |
API_KEY_HEADER |
x-nexgate-key |
Header name for API keys | |
DEFAULT_RATE_LIMIT_ALGORITHM |
sliding_window_log |
Fallback algorithm | |
LOG_QUEUE_NAME |
nexgate:logs |
BullMQ queue name |
| Variable | Required | Default | Description |
|---|---|---|---|
MONGODB_URI |
✅ | — | Same Atlas connection string |
REDIS_URL |
✅ | — | Same Redis URL |
JWT_SECRET |
✅ | — | Min 32 chars, random |
PORTAL_URL |
http://localhost:5173 |
CORS origin | |
API_PORT |
4000 |
Management API port |
Built with ❤️ by Sahil Kumar (Roll: 3252) — NexGate v1.0.0