pythonreactjsnext.jsfastapi

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


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.

Solution

  • The way you defined ethAddress in your endpoint is expected as a query parameter; hence, the 422 Unprocessable Entity error. As per the documentation:

    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 would need to use one of the following options.

    Option 1

    Create a Pydantic model:

    from pydantic import BaseModel
    
    
    class Item(BaseModel):
        eth_addr: str
    
    
    @app.post('/')
    async def add_eth_addr(item: Item):
        return item
    

    FastAPI will expect a body like:

    {
        "eth_addr": "some addr"
    }
    

    Perform HTTP request using Fetch API:

    fetch('/', {
          method: 'POST',
          headers: {
             'Accept': 'application/json',
             'Content-Type': 'application/json'
          },
          body: JSON.stringify({
             "eth_addr": "some addr"
          }),
       })
       .then(resp => resp.json()) // or, resp.text(), etc.
       .then(data => {
          console.log(data); // handle response data
       })
       .catch(error => {
          console.error(error);
       });
    

    Option 2

    Use the Body parameter type:

    from fastapi import Body
    
    
    @app.post('/')
    async def add_eth_addr(eth_addr: str = Body()):
        return {'eth_addr': eth_addr}
    

    FastAPI will expect a body like:

    "some addr"
    

    Perform HTTP request using Fetch API:

    fetch('/', {
          method: 'POST',
          headers: {
             'Accept': 'application/json',
             'Content-Type': 'application/json'
          },
          body: JSON.stringify("some addr"),
       })
       .then(resp => resp.json()) // or, resp.text(), etc.
       .then(data => {
          console.log(data); // handle response data
       })
       .catch(error => {
          console.error(error);
       });
    

    Option 3

    Since you have a single body parameter, you might want to use the special Body parameter embed:

    from fastapi import Body
    
    
    @app.post('/')
    async 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"
    }
    

    Perform HTTP request using Fetch API:

    fetch('/', {
          method: 'POST',
          headers: {
             'Accept': 'application/json',
             'Content-Type': 'application/json'
          },
          body: JSON.stringify({
             "eth_addr": "some addr"
          }),
       })
       .then(resp => resp.json()) // or, resp.text(), etc.
       .then(data => {
          console.log(data); // handle response data
       })
       .catch(error => {
          console.error(error);
       });
    

    Related answers, including JavaScript examples on how to post JSON data, can be found here, here, as well as here and here. This answer might also prove helpful as well, when it comes to posting both JSON data and Files in the same request.