Skip to content

#226: Fine-Tune the OpenAPI Documentation in FastAPI

With FastAPI we get the OpenAPI specification and the Swagger interactive documentation for free. While that is a great help, the automatically generated documentation is not always as helpful as it could be. Let us look for ways in which we can fine-tune the documentation.

Define a return type

With our basic to-do API we currently get this documentation for the create new task endpoint:

The return value of our endpoint is a string

While this is a good start, the documentation thinks the return value is a string. But we return our TaskOutput model, why is that not in the documentation?

It turns out that we missed to define a return type. For the API itself it does not matter, then we get back a JSON response either way. But for the documentation we better specify what we return:

1
2
3
@app.post("/api/todo")
# old: async def create_task(task: TaskInput, request: Request):
async def create_task(task: TaskInput, request: Request) -> TaskOutput:

If we go back to the documentation, we now get a more useful information about the returned data from that endpoint:

We now get the fields of our TaskOutput class as the response of our endpoint.

Return a list

When we return a list, we can define the return type to be a list of a certain type with this code:

1
2
3
4
from typing import List
@app.get("/api/todo")
# old: async def show_all_tasks(filter: Annotated[dict, Depends(filter_parameters)]):
async def show_all_tasks(filter: Annotated[dict, Depends(filter_parameters)]) -> List[TaskOutput]:

In the documentation our type gets wrapped between [ ] to show that it is a list:

The response now shows a list of TaskOutput classes.

Define the HTTP status code

By default, everything in the documentation that is not an error gets a status code of 200. It is not enough to have a different status code in the JSONResponse message, we must put it into the decorator as well:

1
2
3
# old: @app.post("/api/todo")
@app.post("/api/todo", status_code=status.HTTP_201_CREATED)
async def create_task(task: TaskInput, request: Request) -> TaskOutput:

After this change, the status code 201 shows up in the documentation:

The status code is now 201.

Hide an endpoint

We added our main() method to prevent errors when we visit the / route. While this endpoint is not really part of our API, it shows up in the documentation:

The GET / main endpoint shows up in the API documentation.

To prevent that, we can use this marker to hide our endpoint from the documentation:

1
2
3
# old: @app.get("/")
@app.get("/", include_in_schema=False)
async def main():

With his change the endpoint is no longer part of the documentation:

The GET / main endpoint no longer shows up in the API documentation.

Next

With some little tweaks we got a much better documentation. The return types are no longer strings and instead show the fields of our data types, while the endpoints that are not part of the API no longer show up in the documentation.

Next week we look at Bcrypt and how we can hash a password in Python.