How do I download a file from FastAPI backend using Fetch API in the frontend?

First, you need to adjust your endpoint on server side to accept path parameters, as in the way it is currently defined, lat and long are expected to be query parameters; however, in your javascript code you are trying to send those coordinates as path parameters. Thus, your endpoint should look like this:

@app.get("/{lat}/{long}/")
async def read_item(lat: float, long: float):

Next, set the filename in FileResponse, so that it can be included in the Content-Disposition response header, which can later be retrieved on client side:

return FileResponse("/tmp/myics.ics", filename="myics.ics")

If you are doing a cross-origin request (see FastAPI CORS as well), make sure to set the Access-Control-Expose-Headers response header on server side, indicating that the Content-Disposition header should be made available to JS scripts running in the browser; otherwise, the filename won’t be accessible on client side. Example:

headers = {'Access-Control-Expose-Headers': 'Content-Disposition'}
return FileResponse("/tmp/myics.ics", filename="myics.ics", headers=headers)

On client side, you could use a similar approach to this answer or this answer. The below example also takes into account scenarios where the filename includes unicode characters (i.e., -, !, (, ), etc.) and hence, comes (utf-8 encoded) in the form of, for instance, filename*=utf-8''Na%C3%AFve%20file.txt (see here for more details). In such cases, the decodeURIComponent() function is used to decode the filename. Working example below:

const url="http://127.0.0.1:8000/41.64007/-47.285156"
fetch(url)
    .then(res => {
        const disposition = res.headers.get('Content-Disposition');
        filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
        if (filename.toLowerCase().startsWith("utf-8''"))
            filename = decodeURIComponent(filename.replace("utf-8''", ''));
        else
            filename = filename.replace(/['"]/g, '');
        return res.blob();
    })
    .then(blob => {
        var url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a); // append the element to the dom
        a.click();
        a.remove(); // afterwards, remove the element  
    });

Leave a Comment