LJG
LJG

Reputation: 767

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.

Upvotes: 10

Views: 11882

Answers (2)

Chris
Chris

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

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.

Upvotes: 13

cottontail
cottontail

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

Related Questions