0

I'm trying to serve a small web app using Sveltekit and FastAPI.

FastAPI provides an api and various functions and the frontend was built with Sveltekit using adapter-static.

The basic framework is:

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles

app = FastAPI()
api = FastAPI(root_path="/api")

# mount the api
app.mount("/api", api)

# mount the sveltekit static files at root
app.mount('/', StaticFiles(directory="./webapp/build/", html=True), name="webapp")

# define the api endpoints
@api.websocket("/something")
async def do_something(request: Request):
    body = await request.json()
    result = do_something_fancy(body)
    return {"result": result}

...

What I'm having trouble with is that the frontend app defines multiple sub pages:

  • a main page/dashboard at localhost:8888
  • a settings page at localhost:8888/settings
  • an about page at localhost:8888/about

Now if I navigate to any of these pages using my svelte app navbar after starting at the root url "/", everything works fine, but if I navigate directly to http://localhost:8888/settings by entering it in the browser address bar, I get a 404 error and see {"detail":"Not Found"}.

I also get the {"detail":"Not Found"} when I hit "Refresh"/Ctrl-r in my browser when on one of the subpages (settings or about), but it works fine at the root url.

I'm pretty sure I'm just missing something simple like adding routing to the static app mount point, but the FastAPI docs don't specify how this should work (OR, it seems to indicate that it should "just work").

tdpu
  • 400
  • 3
  • 12
  • I thought about doing that way too, but using Sveltekit separately feels much more natural as it is a full-stack framework. You can simply call your backend from the `/.../+server.ts` files. Even for authentication, this looks better to me: you can use AuthJS on the Sveltkit side, and call your backend with your auth choice since the credentials will be kept on the server – HGLR Jul 11 '23 at 12:16
  • Plus you won't have this kind of problems :) – HGLR Jul 11 '23 at 12:17
  • 1
    I guess the way I've set things up, I've tried to simply serve the frontend via FastAPI, rather than try to integrate the backend into the sveltekit framework. I'm not sure which way is "better", but after discovering the issue with my sveltekit build (posted in answer), things are working more or less the way I want. – tdpu Jul 12 '23 at 20:27

2 Answers2

0

I have found a workaround, although it's not exactly what I want and seems non-optimal.

I've gained a bit more understanding about what's going on thanks to this answer: https://stackoverflow.com/a/73113792/13752965

Basically, the only thing that FastAPI knows to serve is the index.html file at my root url /. It seems like I may be able to get what I want by using template responses, but I don't really want templates, I just want to serve the html files that I've generated with my Sveltekit frontend.

My current workaround, is to just redirect back to the main page when requesting one of the subpages:

@app.get('/settings', response_class=RedirectResponse)
async def get_settings():
    return "/"

@app.get('/about', response_class=RedirectResponse)
async def get_about():
    return "/"

# mount the web app
app.mount("/", StaticFiles(directory="./webapp/build/", html=True), name="webapp")

If this is done prior to mounting the static files, then at least the user is sent back to the main page, rather than it looking like the app is broken.

tdpu
  • 400
  • 3
  • 12
0

It was indeed a minor thing.

The problem was that my Sveltekit build was set to output pages directly in the build directory:

  • settings.html
  • about.html

However, the default behaviour for mounting FastAPI (inherited from starlette) StaticFiles objects is to serve index.html pages located at the corresponding url:

  • settings/index.html
  • about/index.html

I didn't appreciate the nuance in this section of the Sveltekit docs: https://kit.svelte.dev/docs/page-options#trailingslash

So, the solution is to add

export const trailingSlash = 'always';

to your routes/+layout.js file. This prompts the Sveltekit build process to output an index.html and folder structure that is required for proper function after mounting in FastAPI.

This answer provides good detail about the FastAPI mount process and many of these "gotchas" that aren't clear in the FastAPI documentation.

tdpu
  • 400
  • 3
  • 12