Skip to content
Advertisement

“422 Unprocessable Entity” error when making POST request with both attributes and key using FastAPI

I have a file called main.py as follows:

JavaScript

Now, if I run the code for the test, saved in the file test_main.py

JavaScript

I don’t get the desired result, that is

JavaScript

What is the mistake?

Advertisement

Answer

Let’s start by explaining what you are doing wrong.


FastAPI’s TestClient is just a re-export of Starlette’s TestClient which is a subclass of requests.Session. The requests library’s post method has the following signature:

JavaScript

Your code

JavaScript

is doing multiple things wrong:

  1. It is passing in arguments to both the data and the json parameter, which is wrong because you can’t have 2 different request bodies. You either pass in data or json, but not both. The data is typically used for form-encoded inputs from HTML forms, while json is for raw JSON objects. See the requests docs on “More complicated POST requests”.
  2. The requests library will simply drop the json argument because:

    Note, the json parameter is ignored if either data or files is passed.

  3. It is passing-in the plain string "Baz" to the json parameter, which is not a valid JSON object.
  4. The data parameter expects form-encoded data.

The full error returned by FastAPI in the response is:

JavaScript

The 1st error says key is missing from the query, meaning the route parameter key value "Baz" was not in the request body and FastAPI tried to look for it from the query parameters (see the FastAPI docs on Request body + path + query parameters).

The 2nd error is from point #4 I listed above about data not being properly form-encoded (that error does go away when you wrap the dict value in json.dumps, but that’s not important nor is it part of the solution).

You said in a comment that you were trying to do the same thing as in the FastAPI Testing Tutorial. The difference of that tutorial from your code is that was POSTing all the attributes of the Item object in 1 body, and that it was using the json= parameter of .post.


Now on the solutions!

Solution #1: Have a separate class for POSTing the item attributes with a key

Here, you’ll need 2 classes, one with a key attribute that you use for the POST request body (let’s call it NewItem), and your current one Item for the internal DB and for the response model. Your route function will then have just 1 parameter (new_item) and you can just get the key from that object.

main.py

JavaScript

For the test .post code, use json= to pass all the fields in 1 dictionary.

test_main.py

JavaScript

Output

JavaScript

Solution #2: Have 2 body parts, 1 for the item attributes and 1 for the key

You can structure the POSTed body like this instead:

JavaScript

where you have the Item attributes in a nested dict and then have a simple key-value pair in the same level as item. FastAPI can handle this, see the docs on Singular values in body, which fits your example quite nicely:

For example, extending the previous model, you could decide that you want to have another key importance in the same body, besides the item and user.

If you declare it as is, because it is a singular value, FastAPI will assume that it is a query parameter.

But you can instruct FastAPI to treat it as another body key using Body

Note the parts I emphasized, about telling FastAPI to look for key in the same body. It is important here that the parameter names item and key match the ones in the request body.

main.py

JavaScript

Again, for making the .post request, use json= to pass the entire dictionary.

test_main.py

JavaScript

Output

JavaScript
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement