A simple solution for organizing your FastAPI endpoints
fastapi-controllers offers a simple solution for organizing your API endpoints by means of a Controller class embracing the concept of class-based views.
- class-based approach to organizing FastAPI endpoints
- class-scoped definition of APIRouter parameters
- instance-scoped definition of FastAPI dependencies
- it integrates seamlessly with the FastAPI framework
- works with both sync and async endpoints
pip install fastapi-controllersimportuvicornfromfastapiimportFastAPI, Response, statusfromfastapi.websocketsimportWebSocketfromfastapi_controllersimportController, get, websocketclassExampleController(Controller):
@get("/example", response_class=Response)asyncdefget_example(self) ->Response:
returnResponse(status_code=status.HTTP_200_OK)
@websocket("/ws")asyncdefws_example(websocket: WebSocket) ->None:
awaitwebsocket.accept()
whileTrue:
data=awaitwebsocket.receive_text()
awaitwebsocket.send_text(f"Received: {data}")
if__name__=="__main__":
app=FastAPI()
app.include_router(ExampleController.create_router())
uvicorn.run(app)FastAPI's APIRouter is created and populated with API routes by the Controller.create_router method and can be incorporated into the application in the usual way via app.include_router.
The router-related parameters as well as those of HTTP request-specific and websocket decorators are expected to be the same as those used by fastapi.APIRouter, fastapi.APIRouter.<request_method> and fastapi.APIRouter.websocket. Validation of the provided parameters is performed during initialization via the inspect module. This ensures compatibility with the FastAPI framework and prevents the introduction of a new, unnecessary naming convention.
fromfastapi_controllersimportdelete, get, head, options, patch, post, put, trace, websocketClass variables can be used to set the commonly used APIRouter parameters: prefix, dependencies and tags.
importuvicornfromfastapiimportDepends, FastAPI, Response, statusfromfastapi.securityimportHTTPBasic, HTTPBasicCredentialsfrompydanticimportBaseModelfromfastapi_controllersimportController, get, postsecurity=HTTPBasic()
asyncdefauthorized_user(credentials: HTTPBasicCredentials=Depends(security)) ->None:
...
classExampleRequest(BaseModel):
name: strclassExampleResponse(BaseModel):
message: strclassExampleController(Controller):
prefix="/example"tags= ["example"]
dependencies= [Depends(authorized_user)]
@get("", response_class=Response)asyncdefget_example(self) ->Response:
returnResponse(status_code=status.HTTP_200_OK)
@post("", response_model=ExampleResponse)asyncdefpost_example(self, data: ExampleRequest) ->ExampleResponse:
returnExampleResponse(message=f"Hello, {data.name}!")
if__name__=="__main__":
app=FastAPI()
app.include_router(ExampleController.create_router())
uvicorn.run(app)Additional APIRouter parameters can be provided via the __router_params__ class variable in form of a mapping.
importuvicornfromfastapiimportFastAPI, Response, statusfromfastapi_controllersimportController, getclassExampleController(Controller):
prefix="/example"tags= ["example"]
__router_params__= {"deprecated": True}
@get("", response_class=Response)asyncdefget_example(self) ->Response:
returnResponse(status_code=status.HTTP_200_OK)
if__name__=="__main__":
app=FastAPI()
app.include_router(ExampleController.create_router())
uvicorn.run(app)
⚠️ Important: Beware of assigning values to the same parameter twice (directly on class-level and through__router_params__). The values stored in__router_params__have precedence and will override your other settings if a name conflict arises. E.g. the followingControllerwould create anAPIRouterwithprefix=/override,tags=["override"]anddependencies=[Depends(override)]
fromfastapiimportDependsfromfastapi_controllersimportControllerclassExampleController(Controller):
prefix="/example"tags= ["example"]
dependencies= [Depends(example)]
__router_params__= {
"prefix": "/override",
"tags": ["override"],
"dependencies": [Depends(override)],
}Instance-scoped attributes can be defined in the __init__ method of the Controller and offer an easy way to access common dependencies for all endpoints.
importjsonimportuvicornfromfastapiimportDepends, FastAPI, Response, statusfromfastapi_controllersimportController, getclassDbSession:
@propertydefstatus(self) ->str:
return"CONNECTED"asyncdefget_db_session() ->DbSession:
returnDbSession()
classExampleController(Controller):
prefix="/example"def__init__(self, session: DbSession=Depends(get_db_session)) ->None:
self.session=session@get("", response_class=Response)asyncdefget_status(self) ->Response:
returnResponse(
content=json.dumps({"status": f"{self.session.status}"}),
status_code=status.HTTP_200_OK,
media_type="application/json",
)
if__name__=="__main__":
app=FastAPI()
app.include_router(ExampleController.create_router())
uvicorn.run(app)