I have a React app that I'm trying to serve statically from FastAPI. I've got all my React build artifacts in my static
folder. So, my FastAPI app looks like this:
static/
assets/
index-lATvXaZG.js
index.html
app.py
In app.py, I have:
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="react")
@app.get("")
@app.get("/")
@app.get("/index.html")
def serve_index():
return FileResponse("static/index.html")
However, when index.html
is loaded, the client understandably makes a GET request for /assets/index-lATvXaZG.js
. Of course, FastAPI can't find this.
Is there a setting I can put in my React app (or, perhaps, in my FastAPI setup) to get the paths lined up?
I should start by highly recommending having a look at this answer and this answer, as the answer posted here is essentially based on what is described in those answers.
The issue should easily be solved by properly using the StaticFiles
instance for such purposes. This means that you should set the html
flag to True
and serve the static files at /
route. That way, you could access the index.html
by simply typing in the address bar of the browser, for instance, http://127.0.0.1:8000/
or http://localhost:8000/
. Hence, any references to /assets/whatever
should work just fine.
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount('/', StaticFiles(directory='static', html=True), name='static')
Keep your files structure as is:
static/
assets/
index.js
index.html
app.py
If you would like to keep your current implementation (that I wouldn't recommend)—meaning that you mount the StaticFiles
instance to /static
path, i.e., app.mount("/static", StaticFiles(directory="static"), name="static")
and keep the html
flag set to False
(which is the default setting)—you could simply add the following line at the top of every HTML file in the static
directory, which essentially specifies the <base>
URL to use for all relative URLs in a document:
<base href="static/">
That way, the prefix static/
should automatically be added to any reference that is made inside the HTML file(s) and solve the issue (the static/
missing from the start of the reference is essentially the reason for not finding the assets
, as you serve them under the /static
route—app.mount("/static",...
).
Alternatively, if you didn't want to add <base href="static/">
to every file, you could have a custom 404
exception_handler
, as demonstrated here, and inside of it get the URL path from the Request
object, as shown here, and then append static/
to the front of the path and return a FileResponse
(if the file exists). Example:
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import FileResponse, PlainTextResponse
from fastapi.staticfiles import StaticFiles
import os
# Same implementation as in your question
app = FastAPI()
app.mount("/static", ...
@app.get("")
...
# The 404 exception handler
@app.exception_handler(404)
async def not_found_exception_handler(request: Request, exc: HTTPException):
filepath = os.path.join('static/', request.url.path[1:])
if os.path.isfile(filepath):
return FileResponse(filepath)
else:
return PlainTextResponse("404 Not Found", status_code=404)
Instead of the custom 404
exception_handler
, one could use an endpoint to serve such asset files (similar to this), by capturing the URL path, as explained here, and appending static/assets/
to the front of it. Hence, any files lying under the assets/
directory, such as assets/index.js
or assets/images/img.png
, would be successfully served.
@app.get("/assets/{asset:path}")
async def get_asset(asset: str):
filepath = os.path.join('static/assets/', asset)
if os.path.isfile(filepath):
return FileResponse(filepath)
else:
return PlainTextResponse("404 Not Found", status_code=404)
Again, this is an approach that I wouldn't really recommend following, and with which one should be very careful, as one could maliciously try to take advantage of it and attempt to access likely restricted files that they are not authorized to do so—that's why os.path.join('static/', ...
is used in the examples above, in order to mitigate such risks and only lookup for files under the static/
directory.
Choosing Option 1 described earlier should be the prefered approach for such purposes.