How to post JSON data from JavaScript frontend to FastAPI backend?

ghz 1years ago ⋅ 5098 views

Question

I am trying to pass a value called 'ethAddress' from an input form on the client to FastAPI so that I can use it in a function to generate a matplotlib chart.

I am using fetch to POST the inputted text in Charts.tsx file:

   fetch("http://localhost:8000/ethAddress", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(ethAddress),
    }).then(fetchEthAddresses);

Then I have my api.py file set up as follows:

#imports
app = FastAPI()

@app.get("/ethAddress")
async def get_images(background_tasks: BackgroundTasks, ethAddress: str):
    
    image = EthBalanceTracker.get_transactions(ethAddress)
    img_buf = image
    background_tasks.add_task(img_buf.close)
    headers = {'Content-Disposition': 'inline; filename="out.png"'}
    return Response(img_buf.getvalue(), headers=headers, media_type='image/png')


@app.post("/ethAddress")
async def add_ethAddress(ethAddress: str):
    return ethAddress

To my understanding, I am passing the 'ethAddress' in the Request Body from the client to the backend using fetch POST request, where I then have access to the value that has been posted using @app.post in FastAPI. I then return that value as a string. Then I am using it in the GET route to generate the chart.

I'm getting this error:

INFO:     127.0.0.1:59821 - "POST /ethAddress HTTP/1.1" 422 Unprocessable Entity
INFO:     127.0.0.1:59821 - "GET /ethAddress HTTP/1.1" 422 Unprocessable Entity

I have also tried switching the fetch method on the client to GET instead of POST. But get the following error:

TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.

Answer

The way you defined ethAddress in your endpoint is expected as a query parameter; hence, the 422 Unprocessable Entity error. As per the [documentation](https://fastapi.tiangolo.com/tutorial/query-params/#query- parameters):

When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.

For the parameter to be interpreted as JSON, you need to either:

Option 1

Create a Pydantic model:

from pydantic import BaseModel

class Item(BaseModel):
    eth_addr: str

@app.post('/ethAddress')
def add_eth_addr(item: Item):
    return item

FastAPI will expect a body like:

{
    "eth_addr": "some addr"
}

Using Fetch API:

//...
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({"eth_addr": "some addr"}),
//...

Option 2

or, use the Body type:

from fastapi import Body

@app.post('/ethAddress')
def add_eth_addr(eth_addr: str = Body()):
    return {'eth_addr': eth_addr}

FastAPI will expect a body like:

"some addr"

Using Fetch API:

//...
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify("some addr"),
//...

Option 3

Since you have a [single body parameter](https://fastapi.tiangolo.com/tutorial/body-multiple-params/#embed- a-single-body-parameter), you might want to use the special Body parameter embed:

from fastapi import Body

@app.post('/ethAddress')
def add_eth_addr(eth_addr: str = Body(embed=True)):
    return {'eth_addr': eth_addr}

FastAPI will expect a body like:

{
    "eth_addr": "some addr"
}

Using Fetch API:

//...
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({"eth_addr": "some addr"}),
//...

Related answers, including JavaScript examples on how to post JSON data as well, can be found here, here, as well as here and here.