33

In my project folder I have a basic index.html file plus static files (js, css) as well as my main.py:

from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi import Request

app = FastAPI()

templates = Jinja2Templates(directory="/")
app.mount("/", StaticFiles(directory="/"))

@app.get("/")
def serve_home(request: Request):
    return templates.TemplateResponse("index.html", context= {"request": request}) 

How can I make fastapi work here? I just want my index.html and static files served on localhost. Is it problematic not to have a static or templates folder?

do-me
  • 1,600
  • 1
  • 10
  • 16

2 Answers2

61

Option 1: Static file mounting

It was easier than expected. Just needed to put all static files including index.html into static folder in project directory and mount static files.

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

That's it. My index.html is now available under http://localhost:8000/static/index.html.

In case it should be accessible right under http://localhost:8000/ without /static and the .html ending one needs to change two things. First it needs to be mounted on / instead of /static and an HTML flag must be set to true when mounting. So just use this line instead:

app.mount("/", StaticFiles(directory="static",html = True), name="static")

(Thanks to this answer)

The index.html is now available under http://localhost:8000/


Option 2: Serving only index.html

As fastapi is based on starlette, a simple FileResponse does the job.

from starlette.responses import FileResponse 

@app.get("/")
async def read_index():
    return FileResponse('index.html')

Found here.

do-me
  • 1,600
  • 1
  • 10
  • 16
  • 1
    I find that pretty baffling tbh. – MichaelRSF Sep 13 '21 at 19:38
  • 2
    Would it help if I said you can import `FileResponse` from `fastapi.responses`? It's just a class that fastapi's rendering mechanism will see as the contents of file _index.html_. – holdenweb Jan 05 '22 at 23:36
  • Option 1 caused my server to return `405 Method Not Allowed` on post requests, just a warning – Arye P. Aug 10 '22 at 05:22
  • @AryeP. that's expected as mounted static files should only allow for GET requests. Why would you fire a POST request for static files anyway? – do-me Aug 11 '22 at 12:39
  • 1
    @do-me I used a POST request on a different rout, not root. – Arye P. Aug 11 '22 at 17:28
  • 2
    Please be aware that the order in which endpoints (as well as independent applications, such as `StaticFiles`) are defined **matters**. Please have a look at [this answer](https://stackoverflow.com/a/73113792/17865804) and [this answer](https://stackoverflow.com/a/74498663/17865804) for more details. – Chris May 18 '23 at 13:02
11

The best and most simple solution that worked for me:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

api_app = FastAPI(title="api app")

@api_app.post("/set_influencers_to_follow")
async def set_influencers_to_follow(request):
    return {}

app = FastAPI(title="main app")

app.mount("/api", api_app)
app.mount("/", StaticFiles(directory="ui", html=True), name="ui")

If project structure is like:

├── main.py
├── ui
│   ├── index.html
│   ├── style.css
│   ├── script.js
  • 1
    It's better to separate your api routes from the route used for static files, I edited your answer for it. – Arsham Arya Apr 03 '22 at 14:27
  • I got a problem with `instance order`, I solved by placing `api_app = FastAPI(title="api app")` after `app = FastAPI(title="main app")`. Details: https://stackoverflow.com/a/75045872/7009215 – Sabbir Sobhani Jan 08 '23 at 06:59