Application server

So we've got our FastAPI application up and running. We've built the bare bones of our first endpoint (which we're not done with yet). And we've also written some tests to hit our endpoint as well as against the underlying business logic.


OpenAPI specification

Wouldn't it be great if we could visualise our API in a standardised way?

This is where the OpenAPI specification comes into play. You might have heard this being refered to as 'swagger' docs. The OpenAPI specification is a standardised way of presenting REST APIs. Specifications can be written in YAML or JSON, they are consumed and then presented back to us in the OpenAPI format.

We've also not written any such specification.

Luckily for us, our chosen framework has generated it on our behalf based on our API code already!


Starting the server

So without further ado, lets drop into the terminal and start the server:

uvicorn main:app --reload

Make sure that your virtual environment is activated before running the above command.

Your terminal will output something that looks like the following after running the uvicorn command:

 uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['<directory where your project is located>']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [5140] using WatchFiles
INFO:     Started server process [5142]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

This means that the application is now being served at the local host address https://127.0.0.1 on port 8000.


Seeing the auto-generated API documentation

So now if we head over to the following address in a browser, we will see the auto-generated OpenAPI documentation for our API:

http://127.0.0.1:8000/docs

This will look something like the following:

This is our API created with the OpenAPI format. Each panel represents an individual endpoint. In our case we just have the 1 single endpoint.

We can also see our API documentation in the Redoc OpenAPI format at http://127.0.0.1:8000/redoc

If we take compare this with our API code:

@app.get(path="/income-taxes")
def calculate_income_taxes(salary: float) -> dict[str, float]:
    tax_owed: float = calculate_income_tax_owed(salary=salary)
    return {"tax owed": tax_owed}

We can see that on line 1 our /income-taxes is used from the path argument we provided to the app decorator.

On line 2, the name of the function forms the generated description which is associated with the endpoint.

If we open up that endpoint in our browser, we can see the expanded tab:

The salary argument has also been rendered as a query parameter to our endpoint. And to top it off, the generated documentation even knows to only take number type values because of how we type hinted the salary argument.

This has the added benefit that there is only 1 single source of truth. Our code. We don't have to maintain a seperate documentation entity in the form of a YAML file or JSON somewhere else that would eventually and quite easily get out of sync. This also reduces our maintenance burden.


Interacting with the API docs

The most valuable part of the generated OpenAPI documentation is that also doubles up as a kind of user interface (UI). We can hit our endpoint via this UI as if we had been curling the API or making a request to it with some other library:

In the above screenshot, we have hit our API with the salary of £33,000. This is the same test case we wrote our test with before.

This UI can be very useful when demonstrating functionality to stakeholders and even for debugging or exploratory purposes.

Speaking of which, why don't you go ahead and see what happens when we input a salary figure of any number between 0 and 12,569...

And so we've found our first bug!

Last updated