Operations keep their upstream tags as the second grouping level, and each service becomes an x-tagGroups entry (first level). Tag names shared by several services are disambiguated with the service name. The intro renders one section per service with the upstream spec's title and info.description (the admin-entered description overrides). The /docs renderer switches from Swagger UI to Scalar, which renders x-tagGroups as a nested sidebar, keeps try-it-out, and gets the API key pre-filled into its authentication panel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
-
Consumer docs —
GET /openapi.json(authenticated withX-API-Key) returns a merged OpenAPI document scoped to that key: every operation the key holds a grant on, across all services, rewritten to the gateway's routes.GET /docsserves a Swagger UI portal around it — paste a key, browse and try exactly what that key can call. -
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).