How to POST a JSON having a single body parameter in FastAPI?

Question:

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.

Asked By: LJG

||

Answers:

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
Answered By: Chris
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.