How to post an image file with a list of strings using FastAPI?

Question:

I have tried a lot of things, but it doesn’t seem to work. Here is my code:

@app.post("/my-endpoint")
async def my_func(
    languages: List[str] = ["en", "hi"], image: UploadFile = File(...)
):

The function works fine when I remove one of the parameters, but with both of the parameters, the retrieved list comes out to be like ["en,hi"], whereas I want it to be ["en, "hi].

I am not even sure if my approach is correct, hence the broader question, if this approach is not right then how can I post a list and an image together?

Asked By: Saransh

||

Answers:

Your function looks just fine. That behaviour though has to do with how FastAPI autodocs (Swagger UI)—I am assuming you are using it for testing, as I did myself and noticed the exact same behaviour—handles the list items. For some reason, the Swagger UI/OpenAPI adds all items as a single item to the list, separated by comma (i.e., ["en, hi, ..."], instead of ["en", "hi", ...]).

Testing the code with Python requests and sending the languages’ list in the proper way, it works just fine. To fix, however, the behaviour of Swagger UI, or any other tool that might behave the same, you could perform a check on the length of the list that is received in the function, and if it is equal to 1 (meaning that the list contains a single item), then split this item using comma delimiter to get a new list with all languages included.

Below is a working example:

app.py

from fastapi import File, UploadFile, FastAPI
from typing import List

app = FastAPI()

@app.post("/submit")
def submit(languages: List[str] = ["en", "hi"], image: UploadFile = File(...)):
        if (len(languages) == 1):
            languages= [item.strip() for item in languages[0].split(',')]
        return {"Languages ": languages, "Uploaded filename": image.filename}

test.py

import requests

url = 'http://127.0.0.1:8000/submit'
image = {'image': open('sample.png', 'rb')}
#payload ={"languages": ["en", "hi"]}  # send languages as separate items
payload ={"languages": "en, hi"}  # send languages as a single item
resp = requests.post(url=url, data=payload, files=image) 
print(resp.json())
Answered By: Chris

I solved this using Query parameters! This might be helpful for someone, though I think Chris’ answer makes much more sense –

@app.post("/my-endpoint")
async def my_func(
    languages: List[str] = Query(["en", "hi"]), image: UploadFile = File(...)
):
Answered By: Saransh