pythonjsonrequestfastapi

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


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.


Solution

  • 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()
    

    Update

    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.