diff --git a/docs/en/docs/tutorial/frontend.md b/docs/en/docs/tutorial/frontend.md new file mode 100644 index 000000000..e0675c4b0 --- /dev/null +++ b/docs/en/docs/tutorial/frontend.md @@ -0,0 +1,119 @@ +# Frontend { #frontend } + +You can serve static frontend apps with `app.frontend()` (or `router.frontend()`). + +This is useful for frontend tools that generate static files, like React with Vite, TanStack Router, Astro, Vue, Svelte, Angular, Solid, and others. + +With these tools, you normally have a step that builds the frontend, with a command like: + +```bash +npm run build +``` + +That would generate a directory like `./dist/` with your frontend files. + +You can use `app.frontend()` to serve that directory following the conventions needed by these frontend frameworks. + +**FastAPI** checks *path operations* first. The frontend files are checked only if no normal route matched, so your API won't be affected. + +## Serve a Frontend { #serve-a-frontend } + +After building your frontend, for example with `npm run build`, put the generated files in a directory, for example, `dist`. + +Your project structure could look like this: + +```text +. +├── pyproject.toml +├── app +│ ├── __init__.py +│ └── main.py +└── dist + ├── index.html + └── assets + └── app.js +``` + +Then serve it with `app.frontend()`: + +{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *} + +With this, a request for `/assets/app.js` can serve `dist/assets/app.js`. + +If you also have a **FastAPI** *path operation*, the *path operation* wins. + +## Client-Side Routing { #client-side-routing } + +Many frontend apps, including **single-page apps** (SPAs), use client-side routing. A path like `/dashboard/settings` might not be a real file but the framework would take care of handling it. + +So, if accessing that URL directly (instead of navigating through the app), the backend should serve the frontend app from `index.html`, so that the frontend framework can then handle the client-side routing. + +For that, use `fallback="index.html"`: + +{* ../../docs_src/frontend/tutorial002_py310.py hl[5] *} + +**FastAPI** uses this fallback only for requests that look like browser navigation. Missing files like JavaScript, CSS, and images still return `404`. + +/// tip + +By default, `fallback` has a value of `fallback="auto"`. In most cases you won't need to specify `fallback`. Read below for details. + +/// + +This is what you would want with many frontend apps that use client-side routing, for example, React with TanStack Router, Vue, Angular, SvelteKit, or Solid. + +## Custom 404 Page { #custom-404-page } + +You can also serve a static `404.html` page for missing frontend paths: + +{* ../../docs_src/frontend/tutorial003_py310.py hl[5] *} + +That response keeps a status code of `404`. + +In this case, **FastAPI** won't serve `index.html` for missing frontend paths. It will return the `404.html` file instead. + +/// tip + +By default, `fallback` has a value of `fallback="auto"`. With this, if a `404.html` file is found, it will be used as the fallback automatically. + +So, you can normally omit the `fallback` argument. + +/// + +This is useful with frontend tools that generate static HTML files for each page, like Astro. + +## Fallback Auto { #fallback-auto } + +By default, `app.frontend()` uses `fallback="auto"`. + +If there is a `404.html` file in the frontend directory, missing frontend paths serve that file with status code `404`. + +Otherwise, if there is an `index.html` file, missing browser navigation paths serve `index.html`, which is what many frontend apps with client-side routing expect. + +So, in most cases you can use `app.frontend("/", directory="dist")` without specifying the `fallback` argument. + +{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *} + +## Disable Fallback { #disable-fallback } + +If you don't want to serve a fallback file for missing frontend paths, use `fallback=None`: + +{* ../../docs_src/frontend/tutorial005_py310.py hl[5] *} + +Then missing frontend paths return the normal `404`. + +## Use it with `APIRouter` { #use-it-with-apirouter } + +You can also add frontend files to an `APIRouter` and include it with a prefix: + +{* ../../docs_src/frontend/tutorial004_py310.py hl[6,7] *} + +In this example, frontend paths are served under `/app`. + +Any regular *path operations* in the app will still take precedence, including in other routers. + +## Static Build Output Only { #static-build-output-only } + +`app.frontend()` serves files already generated by your frontend build. + +It does not run server-side rendering. It is for frontend frameworks that generate static files, not for frameworks that need dynamic rendering on the server for each request. diff --git a/docs/en/docs/tutorial/static-files.md b/docs/en/docs/tutorial/static-files.md index 0b73a35c1..4b5057c08 100644 --- a/docs/en/docs/tutorial/static-files.md +++ b/docs/en/docs/tutorial/static-files.md @@ -2,6 +2,14 @@ You can serve static files automatically from a directory using `StaticFiles`. +/// tip + +If you need to host a frontend, use `app.frontend()` instead, read about it in [Frontend](frontend.md). + +`app.frontend()` uses `StaticFiles` underneath, with several additional advantages for frontends, like handling client-side routing. + +/// + ## Use `StaticFiles` { #use-staticfiles } * Import `StaticFiles`. diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 6393f3844..ec6a5d2aa 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -133,6 +133,7 @@ nav: - tutorial/server-sent-events.md - tutorial/background-tasks.md - tutorial/metadata.md + - tutorial/frontend.md - tutorial/static-files.md - tutorial/testing.md - tutorial/debugging.md