David Y.
—I have a Python script that sends a POST request to an API I’ve built using FastAPI. The POST request fails with error code 422 and the following details:
{ "detail": [ { "loc": [ "body", "firstname" ], "msg": "field required", "type": "value_error.missing" }, { "loc": [ "body", "lastname" ], "msg": "field required", "type": "value_error.missing" }, { "loc": [ "body", "age" ], "msg": "field required", "type": "value_error.missing" } ] }
My client script looks like this:
import requests response = requests.post('http://localhost:8000/create-user/', data={ "firstname": "Robert", "surname": "De Niro", "age": 80 }) print("Response Status Code:", response.status_code) print(response.json())
The relevant API endpoint and Pydantic model look like this:
from pydantic import BaseModel from fastapi import FastAPI app = FastAPI() class User(BaseModel): firstname: str surname: str age: int @app.post("/create-user/") async def create_users(user: User): # ... user creation operations ... return {"Message": "User created."}
What’s going wrong? Is it a problem with my client script or my API code?
The problem is in the client script. An argument passed to the data
parameter of requests.post
will be submitted as form data, but the API expects JSON – this is why the error message seems to indicate that no data was received.
We can solve this by using the json
parameter instead of the data
parameter in our function call:
import requests response = requests.post('http://localhost:8000/create-user/', json={ "firstname": "Robert", "surname": "De Niro", "age": 80 }) print(response.json())
This small change will cause requests
to send our dictionary as JSON data, which should make our client and API endpoint work together as expected.
Alternatively, we could modify the FastAPI endpoint to accept form data, but this requires a few more code modifications. Following the FastAPI documentation, we could install python-multipart
:
pip install python-multipart
Then, we can modify the FastAPI endpoint to accept form data:
from pydantic import BaseModel from fastapi import FastAPI, Form # also import Form from typing import Annotated # new import app = FastAPI() @app.post("/create-user/") async def create_users(firstname: Annotated[str, Form()], surname: Annotated[str, Form()], age: Annotated[int, Form()]): # ... user creation operations ... return {"Message": "User created."}
Note that instead of relying on the fields defined in our Pydantic model, we need to define each field we expect this endpoint to accept. This may be useful if we’re building an endpoint that should only receive some of the fields in a particular model. It is also useful if we would like our endpoint to be used by a simple HTML form rather than a client submitting JSON data.
For more about using Annotated
, please see this answer.
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at compliance@sentry.io.
If you are a California resident, see our Supplemental notice.