Routing
Once you have created your response schemas you can define the SchemaAPIRoute that will be used for your API set.
A SchemaAPIRoute just inherits from fastapi.routing.APIRoute.
from fastapi_responseschema import SchemaAPIRoute
from .myschemas import StandardResponseSchema # The response schema you defined
class StandardAPIRoute(SchemaAPIRoute):
response_schema = StandardResponseSchema # This attribute is required
If you want to handle different schemas for success and error responses you can set the error response schema.
from fastapi_responseschema import SchemaAPIRoute
from .myschemas import OKResponseSchema, KOResponseSchema # The response schemas you defined
class StandardAPIRoute(SchemaAPIRoute):
response_schema = OKResponseSchema # This attribute is required
error_response_schema = KOResponseSchema # If not set defaults to `SchemaAPIRoute.response_schema`
Integrating in your API
You can set the defined SchemaAPIRoute in you FastAPI application.
from fastapi import FastAPI
from .myroutes import StandardAPIRoute # the SchemaAPIRoute you defined
app = FastAPI()
app.router.route_class = StandardAPIRoute
@app.get("/")
def just_a_route():
return {"message": "It Works!"}
You can even integrate the schema api route in APIRouter.
from pydantic import BaseModel
from fastapi import APIRouter
from .myroutes import StandardAPIRoute # the SchemaAPIRoute you defined
router = APIRouter(route_class=StandardAPIRoute)
class ParrotMessage(BaseModel):
message: str
@router.post("/parrot")
def repeat(body: ParrotMessage):
return {"parrot_says": body.message}
Handling errors
You can wrap all error responses with the wrap_error_responses helper.
from fastapi import FastAPI
from fastapi_responseschema import wrap_error_responses
from .myroutes import StandardAPIRoute
from .myschemas import KOResponseSchema
app = FastAPI()
app.router.route_class = StandardAPIRoute
wrap_error_responses(app, error_response_schema=KOResponseSchema)
@app.get("/")
def just_a_route():
return {"message": "It Works!"}
Handling errors and override application APIRoute
The same functionality as:
...
app.router.route_class = StandardAPIRoute
wrap_error_responses(app, error_response_schema=KOResponseSchema)
...
Can be achieved with wrap_app_responses:
from fastapi import FastAPI
from fastapi_responseschema import wrap_app_responses
from .myroutes import StandardAPIRoute # the SchemaAPIRoute you defined
app = FastAPI()
wrap_app_responses(app, route_class=StandardAPIRoute)
@app.get("/")
def just_a_route():
return {"message": "It Works!"}
You still need to configure the route class for every
fastapi.APIRouter.
About response_model_exclude, response_model_include and others response_model_* parametrs
When using response fields modifiers on-the-fly. you must consider that the final output of response_model will be wrapped by the configured ResponseSchema.
For this snippet:
from typing import TypeVar, Generic
from pydantic import BaseModel
from fastapi import APIRouter
from fastapi_responseschema import AbstractResponseSchema, SchemaAPIRoute
T = TypeVar("T")
class ResponseSchema(AbstractResponseSchema[T], Generic[T]):
data: T
error: bool
message: Optional[str]
... # constructors etc.
class Item(BaseModel):
id: int
name: str
additional_desc: Optional[str]
class MainAPIRoute(SchemaAPIRoute):
response_schema = ResponseSchema
router = APIRouter(route_class=MainAPIRoute)
@router.get("/item", response_model=Item)
def show_item():
return {"id": 11, "name": "Just a Teapot!"}
The resulting response payload of GET /items will be:
{
"data": {
"id": 11,
"name": "Just a Teapot!",
"additional_desc": null
},
"error": false,
"message": null
}
When applying the response_model_exclude and additional_model_include for the response_model remeber to consider the nested output.
For Example:
...
@router.get("/item", response_model=Item, response_model_exclude={"data": {"name"}}) # Exclusion of nested fields
def show_item():
return {"id": 11, "name": "Just a Teapot!"}
{
"data": {
"id": 11,
"additional_desc": null
},
"error": false,
"message": null
}
When you use response_model_exclude_none and similar parameters the configuration will be applyed to all the response schema.
For example:
...
@router.get("/item", response_model=Item, response_model_exclude_none=True) # Exclusion of nested fields
def show_item():
return {"id": 11, "name": "Just a Teapot!"}
{
"data": {
"id": 11,
"name": "Just a Teapot!"
},
"error": false
}
To modify the response content you should prefer the definition of dedicated models.