Releases: jirikuncar/ffroute
Release list
v0.1.4 — FFAPIRouter as a static class
New: `ffroute.FFAPIRouter` — the dynamic FFRouter × APIRouter mixin that `speedup()` synthesized at runtime is now a real importable top-level class.
```python
from fastapi import FastAPI
from ffroute import FFAPIRouter
app = FastAPI()
... register routes ...
new_router = FFAPIRouter()
new_router.routes.extend(app.router.routes)
app.router = new_router
app.router._rebuild_index()
```
Why this matters
`FFAPIRouter` is the static type that backs an upcoming proposal to add a `router_class=` kwarg to `Starlette.init` and `FastAPI.init`. Once that lands upstream, the snippet above collapses to:
```python
app = FastAPI(router_class=FFAPIRouter)
```
Shipping the class now (in advance of the upstream PR) lets the proposal cite a real consumer.
Install
```bash
pip install 'ffroute[fastapi]'
```
(Or `uv add 'ffroute[fastapi]'`.) FastAPI is pulled in as a lazy optional dependency — `from ffroute import Router` / `FFRouter` still works without FastAPI installed.
Tests
3 new verification tests (importability + drop-in via assignment + works-without-speedup-call). `speedup()` itself is unchanged — the dynamic-mixin path stays as a fallback for arbitrary user-supplied router subclasses.
Migration
No breaking changes. Existing `speedup(app)` callers keep working unchanged.
v0.1.3
Fix: trie fast-path now engages inside mounted sub-apps
FFRouter.app matched the trie against scope['path'], but Starlette's Mount does not rewrite scope['path'] — it appends the consumed prefix to scope['root_path'] and leaves path as the full request path. A mounted FFRouter therefore fed the trie the full path (/outer/x) while its trie was built from relative patterns (/x), so every lookup missed and dispatch silently fell through to the slow super().app() linear scan.
This is the same dormancy class as the 0.1.2 middleware_stack fix: results stayed correct, but the fast-path went unused inside mounts.
Fix: use get_route_path(scope), which strips root_path — exactly what Route.matches uses internally. Includes a regression test.
Full Changelog: v0.1.2...v0.1.3
v0.1.2
v0.1.1
First published release of ffroute — a Starlette/FastAPI-compatible URL route matcher implemented in Rust with PyO3.
100–200× faster than Starlette's linear regex scan on real-world OpenAPI specs (Stripe, GitHub, Kubernetes), with zero divergences against Starlette's own compile_path oracle.
Highlights
ffroute.Router— low-level trie matcher (zero Python deps).ffroute.FFRouter— drop-instarlette.routing.Routersubclass.ffroute.speedup(app)— one-liner; recurses intoMountsub-apps by default; dynamic-mixin preserves FastAPIAPIRouterhooks (incl./openapi.json).- Wheels published for CPython 3.10–3.14 on Linux (manylinux + musllinux, x86_64 + aarch64), macOS (x86_64 + arm64) and Windows (x86_64).
Notes on v0.1.0
v0.1.0 was tagged but never produced wheels — its CI pipeline hung on a deprecated macOS runner. v0.1.1 ships on the fixed pipeline (macos-14 with cross-compiled Intel wheels; bumped action versions).
v0.1.0
Full Changelog: https://github.com/jirikuncar/ffroute/commits/v0.1.0