A FastAPI reverse proxy that streams every request and response without ever buffering a body in full, with hot-reloadable routing, per-route auth, Redis token-bucket rate limiting, and a per-upstream circuit breaker.
Nobody has started this yet — be first.
No published tickets in this project yet.
About this project
A production-style FastAPI dynamic reverse proxy / API gateway. It sits in front of one or more upstream services, decides where to send a request based on a hot-reloadable route table (flat list, longest-path-prefix wins), and applies auth, rate limiting, header/path transformation, and circuit-breaking per route — all without ever materializing a request or response body as a single object, regardless of size: the request body forwarded upstream is , never , and the response is opened with and streamed back chunk by chunk as yields, so proxying a 1KB response and a 700MB one cost the gateway the same, small, constant amount of memory. Every proxied request goes through the same ordered pipeline — match route, check method, authenticate (pluggable per route: none / api_key / bearer), rate limit (Redis token bucket via one atomic Lua , keyed by route + client), acquire the per-upstream circuit breaker, forward, then record the outcome — and each stage can reject the request before any more expensive stage runs. The route table is reloadable from disk ( + ) or live via a full admin CRUD API (/, plus to write the in-memory table back to disk), none of it requiring a restart.
Clone it
$git clone https://github.com/nishant1821/FastAPIDevTraining.gitHow to run it locally, step by step.
bytesrequest.stream()await request.body()client.send(request, stream=True)response.aiter_raw()EVALroutes.yamlPOST /admin/routes/reloadPUTDELETE /admin/routes/{id}persistFifteen practice tickets are injected into an otherwise-working codebase: nine are real, minimal, single-mistake bugs (an inverted boolean, a swapped comparison operator, a wrong-order pair of path transforms, a silently-broadened except, a division never guarded against its one degenerate input, a check-then-act pair separated by a genuine await that reintroduces a race a lock was supposed to prevent), and six are half-built enhancements — a schema field with no code reading it yet, a function that always returns one hardcoded value, an endpoint that always 501s, a metric that is defined but never .set() — the smallest thing that compiles, never a disguised version of the real fix. Ordered easy to hard, each ticket has its own currently-failing pytest test that goes green when (and only when) that ticket is fixed; no solutions are given anywhere in the ticket docs. A few of the injected bugs are real enough that they also break tests in the project's own main suite (eight of them, as of writing) — expected, not a second problem to chase separately, since fixing the ticket fixes both. The hardest ticket, TICKET-15, is a genuine, reproducible asyncio concurrency race in the circuit breaker's half-open probe admission, verified by deliberately restoring the atomic fix and confirming the test goes green before reverting it back to the buggy version.