Reputation: 767
I have a file called main.py
in which I put a POST
call with only one input parameter (integer). Simplified code is given below:
from fastapi import FastAPI
app = FastAPI()
@app.post("/do_something/")
async def do_something(process_id: int):
# some code
return {"process_id": process_id}
Now, if I run the code for the test, saved in the file test_main.py
, that is:
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_do_something():
response = client.post(
"/do_something/",
json={
"process_id": 16
}
)
return response.json()
print(test_do_something())
I get:
{'detail': [{'loc': ['query', 'process_id'], 'msg': 'field required', 'type': 'value_error.missing'}]}
I can't figure out what the mistake is. It is necessary that it remains a POST
call.
Upvotes: 10
Views: 11882
Reputation: 34551
The error, basically, says that the required query parameter process_id
is missing. The reason for that error is that you send a POST request with request body
, i.e., JSON payload; however, your endpoint expects a query parameter.
To receive the data in JSON format instead, one needs to create a Pydantic BaseModel—as shown below—and send the data from the client in the same way you already do.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
process_id: int
@app.post("/do_something")
async def do_something(item: Item):
return item
Test the above as shown in your question:
def test_do_something():
response = client.post("/do_something", json={"process_id": 16})
return response.json()
If, however, you had to pass a query parameter, then you would create an endpoint in the same way you did, that is:
@app.post("/do_something")
async def do_something(process_id: int):
return {"process_id": process_id}
but on client side, you would need to add the parameter to the URL itself, as described in the documentation (e.g., "/do_something?process_id=16"
), or use the params
attribute and as shown below:
def test_do_something():
response = client.post("/do_something", params={"process_id": 16})
return response.json()
Alternatively, another way to pass JSON data when having a single body parameter is to use Body(..., embed=True)
, as shown below:
@app.post("/do_something")
def do_something(process_id: int = Body(..., embed=True)):
return process_id
For more details and options on how to post JSON data in FastAPI, please have a look at this answer and this answer.
Upvotes: 13
Reputation: 23321
If you want to pass a query parameter in a post request, you can do so by passing it as params=
. In this case, we define the endpoint exactly as in the OP. A working code:
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.post("/do_something/")
async def do_something(process_id: int): # <--- same as in OP
# some code
return {"process_id": process_id}
client = TestClient(app)
response = client.post(
"/do_something/",
params={"process_id": 16} # <--- different here
)
print(response.json())
Another option is to change how we extract data in the endpoint. Instead of type-hinting for an integer, type hint for a dictionary and get the response_id from the received dict. In this case, we make the post request exactly as in the OP. A working code:
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.post("/do_something/")
async def do_something(item: dict[str, int]): # <--- expect a dict
process_id = item.get("process_id") # <--- get process_id
# some code
return {"process_id": process_id}
client = TestClient(app)
response = client.post(
"/do_something/",
json={"process_id": 16} # <--- same as in OP
)
print(response.json())
Of course, we can replace dict[str, int]
above with a TypedDict
sub-class as well.
Upvotes: 0