3

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.

Chris
  • 18,724
  • 6
  • 46
  • 80
LJG
  • 601
  • 1
  • 7
  • 15

1 Answers1

6

The error basically says that, the required query parameter process_id is missing. The reason 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, 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")
def do_something(item: Item):
    return item

If, however, you need to pass a query parameter, then you create an endpoint in the same way you did, but on client side, you need to add the parameter to the URL itself, as described in the documentation and as shown below:

def test_do_something():
    response = client.post("/do_something?process_id=16")
    return response.json()

Update

Alternatively, you can pass a single body parameter using Body(..., embed=True), as shown below (for more details and options on how to post JSON data, see this answer and this answer):

@app.post("/do_something")
def do_something(process_id: int = Body(..., embed=True)):
    return process_id
Chris
  • 18,724
  • 6
  • 46
  • 80
  • Is it possible to do this without creating a Pydantic model? Normally, if I have multiple parameters, I use, for instance: `async def do_something_else(items: List[Dict], process_id: int = Body(...))` in the definition and `client.post("/do_something_else/", json={"process_id": ..., "items": [{...}, {...}]})` for the call. – LJG Jan 19 '22 at 16:53
  • 1
    But `Body(...)` doesn't work if I have only one numeric parameter, as in the example in my question. – LJG Jan 19 '22 at 18:16