fastapi-cbv-router
Class-based views (CBV) for FastAPI routers — a small, maintained drop-in
replacement for the unmaintained fastapi_utils.cbv. Works on modern FastAPI
(tested from 0.115), and in particular survives FastAPI 0.137+, where
fastapi_utils.cbv breaks.
Why
fastapi_utils.cbv relied on APIRouter.include_router eagerly copying
APIRoute objects into router.routes. FastAPI 0.137 made include_router
lazy, which broke fastapi_utils whenever two @cbv decorators shared one
router.
fastapi-cbv-router rebuilds each endpoint through the router's public
add_api_route / add_api_websocket_route API, which stays eager and
delegates all route-state computation back to FastAPI. Route configuration is
copied by introspecting add_api_route's own parameters, so the package adapts
to upstream FastAPI changes instead of hard-coding a kwarg list.
Features
- One small decorator,
@cbv(router)— no base classes or metaclasses required. - Share a single dependency instance (
self) across every endpoint on a class. - Inject dependencies either as class-level annotated attributes or through a
custom
__init__— use whichever fits your style. - Works with regular HTTP routes and WebSocket routes.
- Preserves all route configuration (
status_code,response_model,dependencies, tags, …) and the routerprefix. - Fully typed (
py.typed), zero dependencies beyond FastAPI.
Requirements
- Python: 3.12+
- FastAPI: 0.115+ — CI runs the test suite against Python 3.12/3.13 crossed
with FastAPI 0.115.0, 0.137.2, and the latest release. The suite specifically
covers FastAPI 0.137+, where
include_routerbecame lazy and brokefastapi_utils.cbv.
Install
pip install fastapi-cbv-router
# or
uv add fastapi-cbv-router
Quickstart
from fastapi import APIRouter, Depends, FastAPI
from fastapi_cbv_router import cbv
router = APIRouter(prefix="/items")
def get_db() -> str:
return "db-connection"
@cbv(router)
class ItemsView:
db: str = Depends(get_db)
@router.get("/")
def list_items(self) -> dict:
return {"db": self.db, "items": []}
@router.post("/")
def create_item(self, name: str) -> dict:
return {"db": self.db, "created": name}
app = FastAPI()
app.include_router(router)
Class-level annotated attributes become keyword-only dependencies injected via
Depends(ItemsView) and are available as self.<attr> in every endpoint.
ClassVar-annotated attributes are treated as plain class constants and are not
injected.
Dependency injection via __init__
If you prefer constructor injection — for example to keep a clean abstract base
and wire dependencies explicitly — define a custom __init__. Its parameters
are resolved by FastAPI when the view is constructed, exactly like an endpoint's
parameters:
import abc
from http import HTTPStatus
from fastapi import APIRouter, Depends
from fastapi_cbv_router import cbv
router = APIRouter(prefix="/items", tags=["items"])
def get_service() -> "ItemService":
...
class ItemApi(abc.ABC):
@abc.abstractmethod
async def get_item(self, item_id: int) -> dict: ...
@cbv(router)
class HttpItemApi(ItemApi):
def __init__(self, service: "ItemService" = Depends(get_service)) -> None:
self.service = service
@router.get("/{item_id}", status_code=HTTPStatus.OK)
async def get_item(self, item_id: int) -> dict:
return await self.service.fetch(item_id)
This pairs cleanly with DI containers such as
dependency-injector: use
Depends(Provide[...]) defaults on the @inject-decorated __init__.
WebSocket routes
WebSocket endpoints are supported the same way as HTTP routes:
from fastapi import APIRouter, WebSocket
from fastapi_cbv_router import cbv
router = APIRouter()
@cbv(router)
class ChatView:
@router.websocket("/ws")
async def chat(self, websocket: WebSocket) -> None:
await websocket.accept()
await websocket.send_text("hello")
await websocket.close()
Scope
Only the plain @cbv(router) form is supported (no *urls / set_responses
helpers). This is intentional — it covers the common case with the smallest,
most maintainable surface.
License
MIT
Release files for fastapi-cbv-router 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_cbv_router-0.1.2.tar.gz | 7.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_cbv_router-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:15.3 kB
Release files / fastapi_cbv_router-0.1.2.tar.gz
| Download URL | fastapi_cbv_router-0.1.2.tar.gz |
|---|---|
| Size | 7.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c110e236295b87e648fadacd64d92dddf5a61033403c9de9835c77a74f72697a
|
|
BLAKE2b-256 checksum How to use checksums |
11fb30325240a62b054e5108b07e1acf91e82e55c841f3c73a54820d3f07addb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fastapi_cbv_router-0.1.2-py3-none-any.whl
| Download URL | fastapi_cbv_router-0.1.2-py3-none-any.whl |
|---|---|
| Size | 7.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e4075fe98a3710fd7b2aed61804a93df037be06769b434a5ab9786ef89a1b53c
|
|
BLAKE2b-256 checksum How to use checksums |
7b57df0023fb28abb45f0fcdf7fb4ca74f73bfdf5cccf4f329eb6ba6d55c501a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|