FastAPI's new app.frontend() fixes a route-order bug in manual SPA serving
Put a FastAPI catch-all route above your API route by mistake and a request to /api/ping returns HTTP 200. The body is your single-page app's index.html , not your JSON. No error, no 404, nothing in the logs that looks wrong. I found this while testing app.frontend() , the new call FastAPI shipped across versions 0.138.0 to 0.141.0, between 20 June and 29 July this year. Serving a single-page app…
FastAPI introduced a new function, app.frontend(), to improve serving single-page applications (SPAs) that automatically fixes a route-order bug in manual serving. This bug would cause requests to /api/ping returning a 200 response with an HTML instead of JSON body, with no error or 404 indication. The issue occurred because the catch-all route was placed below API routes, and Starlette matched routes based on their declaration order.
The new app.frontend() function stores routes separately as _low_priority_routes and checks them only after other path operations fail to match, regardless of where it appears in the file. This ensures that the SPA shell is served correctly even if the route is added later.
Another problem with the old pattern was that it couldn't distinguish between a client-side navigation and a request for a non-existent asset. Both were treated as unmatched GET requests, resulting in the same response of index.html. FastAPI's new function addresses this by checking the Accept header. If it's set to application/json, it returns a 404, while other requests return the SPA shell.
Additionally, the old pattern lacked support for protecting the frontend with authentication and was missing explicit support for HEAD requests, making it less flexible compared to the new approach.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.