Here is a simple static FastAPI app. With this setup even though the root path is expected to return a FileResponse of custom.html, the app still returns index.html. How can I get the root path work and render custom.html?
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
app = FastAPI()
app.mount(
"/",
StaticFiles(directory="static", html=True),
name="static",
)
@app.get("/")
async def index() -> FileResponse:
return FileResponse("custom.html", media_type="html")
As per Starlette documentation:
In addtion, as shown from the code snippet you provided, you have mounted
StaticFilesto the root directory (i.e.,/), instead of, for example,/static(or some other path name), as shown below:As per FastAPI documentation:
Hence, any path that starts with
/will be handled by thatStaticFilesapplication, and due to specifyinghtml=Truein the arguments,index.htmlwill be automatically loaded; regardless of creating a separate endpoint pointing to the root path/and trying to return something else, as demonstrated in the example given in your question.Order Matters
If, for example, you moved
app.mount("/",StaticFiles(...line after defining your@app.get("/")endpoint, you would see that order matters andindex.htmlwould not automatically be loaded anymore, as endpoints are evaluated in order. Note that, in your case, you might get anInternal Server Error, as your@app.get("/")endpoint would be called and attempt to findcustom.html, but if this file is not located under the root/directory, but rather under/staticdirectory (as shown from your code), you would then get aFile does not existerror, and hence, you should instead returnFileResponse('static/custom.html').Even if you removed
html=True, but keepStaticFilesmounted to the root directory and defined before your/endpoint, you would get a{"detail":"Not Found"}error response when attempting to accesshttp://localhost:8000/. This is because the/route would still be handled by theStaticFilesapplication (as mentioned earlier), and you should thus need to specify the file that you would like to access (whenhtml=Trueis not used), e.g.,http://localhost:8000/index.html. Even if you defined other endpoints in your code (e.g.,/register,/login,/hello), as long asStaticFilesis mounted to the root directory (i.e.,/) and defined in your code before all other endpoints, for instance:every request to those routes would again be handled by the
StaticFilesapplication, and hence, would lead to an error response, such as{"detail":"Not Found"}(if you send aGETrequest, such as when you type a URL in the address bar of the browser and then hit the Enter key, and the given path does not match a file name in thestaticweb directory), or{detail": "Method Not Allowed"}(if you issue aPOSTrequest through Swagger UI or some other client platform/application). As described in Starlette's documentation onStaticFiles(seeStaticFilesclass implementation as well):Hence, you should either mount the
StaticFilesinstance to a different/unique path, such as/static(i.e.,app.mount('/static', ..., as shown at the top of this answer), or, if you still want to mount theStaticFilesinstance to/path, defineStaticFilesafter declaring all your API endpoints, for example:Note
Every time a webpage is loaded, the browser caches most content on the page, in order to shorten laod times (the next time that user loads the page). Thus, if you tried out the example provided earlier, i.e., where the
StaticFilesapplication is defined before every API endpoint, and then, using the same browser session, you tried out the example above with theStaticFilesapplication defined after all API endpoints, but the browser still displays the content ofstatic/index.htmlfile instead ofstatic/custom.html—when accessinghttp://localhost:8000/in your browser—this is due to the browser loading the webpage from the cache. To overcome this, you could either clear your browser's cache, or open the webpage in an Incognito window (and close it when you are done with it), or simply press Ctrl+F5, instead of just F5, in your browser (using either an Incognito or regular window), which would force the browser to retrieve the webpage from the server instead of loading it from the cache.You may also find this answer helpful, regarding the order of endpoints in FastAPI.
The
html=TrueoptionSetting the
htmlargument ofStaticFilesinstance toTrue(i.e.,html=True) simply provides an easy way to serve a directory of web content with just one line of code. If you only need to serve static files, such as package docs directory, then this is the way to go. If, however, you need to serve different HTML files that will get dynamically updated, as well as you wish to create additional routes/endpoints, you should better have a look atTemplates(notFileResponse), as well as mount yourStaticFilesinstance to a different path (e.g.,/static), rather than root path (and without usinghtml=True).