A response having a 422
(unprocessable entity
) status code will have a response body that specifies the error message, telling exactly which part of your request is missing or doesn’t match the expected format. The code snippet you povided shows that you are trying to post JSON
data to an endpoint that is expecting user
being a query
parameter, rather than JSON
payload. Hence, the 422 unprocessable entity
error. Below are given four different options on how to define an endpoint to expect JSON
data.
Option 1
As per the documentation, when you need to send JSON
data from a client (let's say, a browser) to your API, you send it as a request body (through a POST
request). To declare a request body, you can use Pydantic models.
from pydantic import BaseModel
class User(BaseModel):
user: str
@app.post('/')
def main(user: User):
return user
Option 2
If one doesn't want to use Pydantic models, they could also use Body parameters. If a single body parameter is used (as in your example), you can use the special Body parameter embed.
from fastapi import Body
@app.post('/')
def main(user: str = Body(..., embed=True)):
return {'user': user}
Option 3
Another (less recommended) way would be to use a Dict
type (or simply dict
in Python 3.9+) to declare a key:value
pair. However, in this way, you can't use custom validations for various attributes in your expected JSON
, as you would do with Pydantic models or Body
fields (e.g., check if an email address is valid, or if a string follows a specific pattern).
from typing import Dict, Any
@app.post('/')
def main(payload: Dict[Any, Any]):
return payload
In the example above, payload
could also be defined as payload: dict[Any, Any]
, or simply payload: dict
.
Option 4
If you are confident that the incoming data is a valid JSON
, you can use Starlette's Request
object directly to get the request body parsed as JSON
, using await request.json()
. However, with this approach, not only can't you use custom validations for your attributes, but you would also need to define your endpoint with async def
, since request.json()
is an async
method and thus, one needs to await
it (have a look at this answer for more details on def
vs async def
).
from fastapi import Request
@app.post('/')
async def main(request: Request):
return await request.json()
If you wish, you could also implement some checking on the Content-Type
request header value, before attempting to parse the data, similar to this answer. However, just because a request says application/json
in the Content-Type
header, it doesn't always mean that this is true, or that the incoming data is a valid JSON
(i.e., may be missing a curly bracket, have a key that does not have a value, etc). Hence, you could use a try-except
block when you attempt to parse the data, letting you handle any JSONDecodeError
, in case there is an issue with the way in which your JSON
data is formatted.
from fastapi import Request, HTTPException
from json import JSONDecodeError
@app.post('/')
async def main(request: Request):
content_type = request.headers.get('Content-Type')
if content_type is None:
raise HTTPException(status_code=400, detail='No Content-Type provided')
elif content_type == 'application/json':
try:
return await request.json()
except JSONDecodeError:
raise HTTPException(status_code=400, detail='Invalid JSON data')
else:
raise HTTPException(status_code=400, detail='Content-Type not supported')
If you would like the endpoint accepting both specific/pre-defined and arbitrary JSON data, please check this answer out.
Test the above options
Using Python requests library
Related answer can be found here.
import requests
url = 'http://127.0.0.1:8000/'
payload ={'user': 'foo'}
resp = requests.post(url=url, json=payload)
print(resp.json())
Related answers can be found here and here as well. For examples using axios
, please have a look at this answer, as well as this answer and this answer.
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({'user': 'foo'})
})
.then(resp => resp.json()) // or, resp.text(), etc
.then(data => {
console.log(data); // handle response data
})
.catch(error => {
console.error(error);
});