February 19, 2024 • 6 min read

FastAPI and Pydantic: A Powerful Duo

Rédigé par Louis Marx

Louis Marx

In this article, find out how FastAPI and Pydantic complement each other. We'll look at :
- the rich data validation rules they offer,
- the automatic documentation they generate and
- the features of your IDE that make them so easy to implement.

You've just finished developing your revolutionary AI to help apprentice gardeners. Because the worldwide excitement is tangible, it's time for you to share your work. You want to make your algorithm accessible in a flexible, convenient and standard way, so you're thinking of an API. You're impatient and want to write as little code as possible, so you need FastAPI!

FastAPI is the backend python framework you need to interface your model with other systems. And with just a few extra lines of code only ! Thanks to its tight integration with Pydantic, you will save yourself hours converting and validating data and even documenting your API. FastAPI and Pydantic were made for each other. Their combination gives you the power to develop high-quality APIs in no time, whether you're experienced or not. Let me show you their main synergies and put you on the road to using FastAPI and Pydantic like professionals.

Before going any further, let's start by installing FastAPI in your project:

pip install fastapi
pip install uvicorn

Uvicorn is a server that will allow you to test your API locally. Let's create a main.py file containing FastAPI's version of "Hello World":

Then run the following command in your terminal:

uvicorn main:app --reload

Data Validation Simplified

It's time to get our hands dirty so that gardens all over the world can soon flourish. One of your routes might look something like this:

In short, it's a path operation and a path operation function that incorporates path, query, and body parameters. These few lines may seem like nothing, but a lot is going on under the bonnet. Actually, most concerns you may encounter when developing an API are handled automatically by FastAPI and Pydantic. Through simple type declarations they deal with parsing input data, converting it if necessary, validating it, returning an error, etc...

It’s all about type annotations

For example, since plant_id is declared as an integer, FastAPI gives you automatic request "parsing". The id provided as a string in the URL will immediately be converted to Python int. And that's not all, the same Python type declaration instructs FastAPI to give you data validation. So the http://127.0.0.1:8000/garden/plants/rose route returns an HTTP error that is explicit enough to understand where the validation error comes from:

Your future users will thank you for it!

In our API route example, we declared body, path, and query parameters, all at the same time. We didn’t worry about whether FastAPI would be able to distinguish between them - it is. And each of these parameters benefits from automatic conversion and validation functions, even our Pydantic plant model!

That's the beauty of the fabulous combination of FastAPI and Pydantic. The plant instance contained in the body of the request is first read as JSON. Then, each of its fields is converted to the right type, and validated before itself generating the instance of the Plant class. To find out more about how validation rules can be used in real projects for greater robustness and better data quality, take a look at this article!

Nested Pydantic models

The advantage is that you can leverage Pydantic's richness to create complex data types and reuse them to validate the data supplied to the API. You can even define and use nested data models arbitrarily, all thanks to Pydantic.

This will expect (convert, validate, document, etc.) a JSON body like:

And you can nest models as deeply as you like. A method that will certainly be useful in your projects is .model_dump, which you can use on a Pydantic object to retrieve its data in the form of a dictionary; it recursively converts sub-models to dictionaries. There is also the jsonable_encoder method, which converts the data to JSON, which is often useful for interacting with a database. As in the first example, the .model_dump method associated with the unpacking operator ** proves formidable to get a Pydantic from the data of another Pydantic model.

Metadata and advanced validation rules

Finally, what you'll probably need to become a pro is to beef up your validation rules a bit, and that's child's play with Pydantic's Field class. Let’s transform our code as follows:

Just like that, we have made sure that the name of the variety does not exceed 300 characters and that its size is greater than zero. These constraints will be applied when the Pydantic model is used by FastAPI and clear validation feedback will be sent back to the user in the event of non-compliance. Wondering what purpose the description and examples parameters serve? Stay with me, and we'll find out in the next section.

To sum up, via simple Python-type declarations, FastAPI manages the data validation that is carried out under the bonnet by Pydantic. This applies to path, query, and body parameters, and works wonders on even complex, nested Pydantic models.

Automatic API Documentation

OpenAPI standard documentation

I've got a little surprise for you, and it's right here http://127.0.0.1:8000/docs! On the sly, FastAPI has generated interactive documentation for you (or rather, its smooth integration of Swagger UI did). That will allow your users to learn how to use your API, which is another precious time-saver. All the routes we've defined and the parameters associated with them are used to define the schema of your API according to the OpenAPI standard. It is precisely this schema, which also includes the data types and validation criteria, that underpins your documentation.

Automatically generated documentation by FastAPI
Automatically generated documentation by FastAPI

It is also in this documentation that the metadata we can add to our parameters (such as description) takes on its full meaning. If we add some to our care_instruction parameter, as shown below:

They appear clearly in our documentation. The “Try it out” button at the top right allows you to call your routes and test them directly from your browser. And as if that wasn't enough, FastAPI even offers you ready-made request bodies, based on your Pydantic type declarations and templates, to make your work easier. Note also that since we've added examples inside our Plant Pydantic model, using Field(examples=["something"]), that example is added to the JSON schema of that Pydantic model. They are then displayed in your documentation user interface.

Interface for trying out queries and viewing validation rules
Interface for trying out queries and viewing validation rules

Use Python enumerations for greater clarity

To conclude this section on automatic documentation of your API, I'd like to share with you a little pro tip that involves using Python enumerations as parameters to your requests, as in the example code below:

This is a typical case where the use of a Python enumeration is relevant. Enumerations are perfect for defining a list of constants, which are not reassignable and can be iterated over. They also support the dot notation to access members, which makes them particularly convenient to use. And the icing on the cake, because the available values for the sunshine parameter are predefined, the interactive docs can show them nicely.

Drop-down menu to select possible values for the sunshine parameter defined as a python enumeration
Drop-down menu to select possible values for the sunshine parameter defined as a python enumeration

Enhanced Editor Support

Auto-completion, type checks, and more

So far, I've shown you the main features that make FastAPI worthy of its name. By using type declarations to convert, validate, and document your API, you avoid wasting time doing these tedious tasks manually. But that's not all, FastAPI and Pydantic are even faster than you might think. Not only do they reduce the amount of code you have to write to deploy your application via an API, but they also make writing the few lines of code you need super easy and fast, thanks to the features of your editor.

Once again, it all starts with the type declaration. Thanks to these, in our editor, inside our function, we have type indications and auto-completion (this wouldn't happen if we received a dict instead of a Pydantic model). And this didn't happen by chance, changes have been made to Pydantic itself to ensure this is the case.

Auto-completion of Pydantic objects in VS Code and many other IDEs
Auto-completion of Pydantic objects in VS Code and many other IDEs

Beyond simple auto-completion, your editor can warn you of type incompatibilities or missing required parameters. If you have installed a static type-checking library like Mypy, then the rules defined by Pydantic and checked at runtime will be checked as you write your code. And all this without having to install any plugins. FastAPI and Pydantic have simply been designed to take advantage of the Python features offered by your editor. Simple and effective.

Fast debugging with VS Code

I can't leave you there without sharing one last pro tip about how to debug your application. If you use VS Code like I do, I'm going to share with you my setup for debugging quickly. To start, use the shortcut Ctrl + P (or ⌘ + P) and type "debug " then select "Python Debugger..." and "Python Debugger: FastAPI". This should create an automatic debugging configuration for your FastAPI application and launch your uvicorn web server locally.

Launching the uvicorn server in debugger mode on VS Code
Launching the uvicorn server in debugger mode on VS Code

At this stage, you can already test your routes in your terminal using the curl method:

Executing an API call via the terminal using the curl method
Executing an API call via the terminal using the curl method

Now, since you're in Debugger mode, you can add breakpoints to your API code so that its execution stops at a desired stage. For example here, I've added a breakpoint to my get_plants_by_sunshine function and when I call the corresponding route (from my terminal or a browser), execution stops and I can inspect the variables.

Inspection of intermediate variables using breakpoint in debugger mode
Inspection of intermediate variables using breakpoint in debugger mode

Note that I've added a browser window to my setup. It's used here to view my API documentation, but I can use it to make GET requests or view my application if it has an interface. To add this window, access the command palette with Ctrl + Shift + P (or ⌘ + ⇧ + P) and type "Simple Browser: Show" to add this window. When prompted, provide a URL, and it will appear.

Conclusion

In wrapping up our exploration of FastAPI and Pydantic, we've uncovered a powerful duo that simplifies API development. FastAPI stands out for its rapid deployment capabilities, significantly reducing boilerplate code and offering automatic documentation. Pydantic complements this by enforcing strict data validation and easing data handling.

We have seen that declaring the types of our parameters and using Pydantic models is all we need to take advantage of this combo. Data validation and documentation are based on these declarations, which is why it's a good idea to expand the metadata of our parameters using the Field function, for example. Finally, many IDEs such as VS Code have built-in pre-configurations for rapidly testing and debugging the code of a FastAPI application.

If you're looking to take your data projects to the next level, discover Sicara's expertise in Data Engineering, and don't hesitate to reach out!

Cet article a été écrit par

Louis Marx

Louis Marx