So far, we used the GET
method to ask FastAPI for data. Now it is time to send data to FastAPI and do some work with it.
This post is part of my journey to learn Python. You find the code for this post in my PythonFriday repository on GitHub.
Pydantic models
FastAPI makes is super simple for us if we follow along and use Pydantic models for our endpoints – while it gets incredibly annoying if we try to go a different way. Therefore, let us follow the official path before we try another way at the end of this post.
Given we have this data snipped that we want to send to our API:
1 2 3 4 5 |
data = { "a": 13, "b": "ABC", "c": True } |
We can create a minimalistic Pydantic model with this code:
1 2 3 4 5 6 |
from pydantic import BaseModel class Item(BaseModel): a: int b: str c: bool |
We can create an instance of our Item class by unpacking our data dictionary:
1 2 3 4 5 6 7 |
>>> item = Item(**data) >>> item.a 13 >>> item.b 'ABC' >>> item.c True |
If we try to use data that does not match our description, Pydantic will throw an exception:
1 |
wrong = Item(a="Hello", b="World", c=False) |
Traceback (most recent call last):
pydantic_core._pydantic_core.ValidationError: 1 validation error for Item a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value=’Hello’, input_type=str]
For further information visit https://errors.pydantic.dev/2.5/v/int_parsing
With only 4 lines of class definition, we got our model and a data validation that checks if the parameters match our expected data types.
Use the Pydantic model with our endpoint
For our endpoint we can use the POST
method and use our model as the parameter:
1 2 3 |
@app.post("/check") async def check(item: Item): return item |
To test if everything works as expected, we can call our /check endpoint with this test:
1 2 3 4 5 6 7 8 9 |
def test_check_with_model(): data = { "a": 13, "b": "ABC", "c": True } response = client.post("/check", json=data) assert response.status_code == 200 assert response.json() == data |
If you want to try it in your browser, head to the Swagger documentation in http://127.0.0.1:8000/docs and select the /check endpoint:
The hard way without Pydantic
This part is only to show you that you can ignore Pydantic and still get data from the request body into your endpoints. However, the code not only looks ugly, but it is also a lot more work:
1 2 3 4 5 6 |
@app.post("/check2") async def check(a: Annotated[int, Body()], b: Annotated[str, Body()], c: Annotated[bool, Body()]): return {"a": a, "b": b, "c": c} |
We can reuse our test from above and only need to change the name of the endpoint we send our data to:
1 2 3 4 5 6 7 8 9 |
def test_check2_without_model(): data = { "a": 13, "b": "ABC", "c": True } response = client.post("/check2", json=data) assert response.status_code == 200 assert response.json() == data |
Next
If we use Pydantic for our models we can keep our FastAPI endpoints nice and compact. While it takes a bit of time to get used to that way, it is much faster than to go the extra mile with Annotated and Body().
As soon as we use models we end up with a mess in our code. Next week we look at a better way to structure our API.
1 thought on “Python Friday #218: Send Data to FastAPI”