- FastAPI async gateway with httpx proxying to multiple upstreams - SQLite database with SQLAlchemy ORM - Admin console: manage services, users, API keys, endpoint access - Per-key, per-endpoint granular access control - OpenAPI document sync and caching (5-minute TTL) - Request/response logging with full transaction inspection - In-memory rate limiting (per-key, fixed-window) - Tiered log retention (7d payloads, 90d rows, incremental vacuum) - TLS verification toggle per service (for self-signed certificates) - Service connectivity validation with automatic endpoint refresh - Request browser with filters and deep-link inspection - Docker setup with persistent volume - Modal forms for create/edit flows Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
4.7 KiB
API Gateway
A FastAPI-based gateway that exposes a single entrypoint for multiple upstream APIs, with a built-in web management console.
Features
-
Single entrypoint — consumers call
/<service-slug>/<path>and the gateway proxies to the registered upstream (all HTTP methods, query strings, bodies, headers)./admin,/static,/docsand/healthare reserved. -
Endpoint catalog, auto-synced — each service's callable endpoints are mirrored from its OpenAPI document (
/openapi.json,/swagger.jsonor/api-docs) whenever the API Keys page loads: new operations appear, removed ones are deleted (their grants with them). Services without a reachable spec keep their existing catalog. Patterns support{param}(one segment),*(within a segment) and**(any depth). -
Per-key access rights, hierarchical picker — every API key is granted a specific set of endpoints, chosen in a path tree (service → path segments → operations) where a parent checkbox selects everything beneath it. A request must match a listed endpoint AND the key must hold a grant on it; otherwise 404 (
unknown_endpoint) or 403 (access_denied). Keys are hashed at rest, shown once at creation, revocable, with per-key rate limits. -
Web management console (
/admin) — manage connected APIs, users, and API keys; usage dashboards with charts. Every console user has full management access. -
Transaction capture & request browser — every gateway request is recorded with service, endpoint, key, status, latency, client IP, query string, and the request/response payloads (textual bodies up to 64 KB; binary summarized). The Requests page filters by service / user / key / status class / path and links to a per-request detail view showing the complete transaction.
-
Usage monitoring — charts: traffic and average latency over time (adaptive buckets: 5 min / 1 h / 1 day), volume by service / user / endpoint, status codes, latency by service, top keys — filterable by time range (1 h – 30 d), service, user and key.
-
Retention — a background purge runs every 6 hours: request/response payloads are blanked after 7 days (the log row stays inspectable), whole log rows are deleted after 90 days, and freed pages are returned to the filesystem via incremental vacuum. Both windows are configurable;
0disables that tier.
Quick start
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000
Open http://localhost:8000/admin — first startup seeds an admin account
(admin / admin by default — change it immediately).
Docker
docker compose up -d
The console is served on http://localhost:8000/admin; the SQLite database
persists in the gateway-data volume. Set GATEWAY_SECRET_KEY (and the admin
credentials) in docker-compose.yml before deploying anywhere real. The image
runs a single uvicorn worker on purpose — the rate limiter and the OpenAPI
sync cache are in-memory.
Configuration (environment variables)
| Variable | Default | Purpose |
|---|---|---|
GATEWAY_DATABASE_URL |
sqlite:///./gateway.db |
SQLAlchemy database URL |
GATEWAY_SECRET_KEY |
dev value | Session cookie signing key — set in production |
GATEWAY_ADMIN_USER / GATEWAY_ADMIN_PASSWORD |
admin / admin |
Seeded admin credentials |
GATEWAY_PAYLOAD_RETENTION_DAYS |
7 |
Blank stored payloads after N days (0 = keep forever) |
GATEWAY_LOG_RETENTION_DAYS |
90 |
Delete log rows after N days (0 = keep forever) |
Using the gateway as a consumer
- Register a service (e.g. slug
weather→https://api.example.com/v1) and list its endpoints (import from OpenAPI or add manually). - Issue an API key and tick the endpoints it may call.
- The consumer calls:
curl -H "X-API-Key: gw_..." "http://localhost:8000/weather/forecast?city=Paris"
The gateway authenticates the key, matches the request against the service's
endpoint list, checks the key's grant and the rate limit, proxies the request to
https://api.example.com/v1/forecast?city=Paris, logs it, and returns the
upstream response. Gateway-generated errors are JSON with an error code:
missing_api_key (401), invalid_api_key / access_denied (403),
unknown_service / unknown_endpoint (404), rate_limited (429),
upstream_unreachable (502), upstream_timeout (504).
Notes & limits
- The stats queries use SQLite date functions; if you point
GATEWAY_DATABASE_URLat Postgres/MySQL, adaptapp/admin/stats.py(strftime,iif). - The rate limiter is in-memory (single process). Run one worker, or swap in Redis for multi-worker deployments.
- Streaming responses are buffered (fine for JSON APIs; not for large file proxying).