agentsclimarketplace

Spa fallback fastapi mount

Skill kjuhwa/skills-hub/skills/fastapi/spa-fallback-fastapi-mount

Self-correcting knowledge corpus for Claude Code — 9 stable shape clusters, bias-correction pipeline baked into contribution flow. 47 papers, 45 techniques, 1.1k skills.

Install
npx -y skills add kjuhwa/skills-hub --skill spa-fallback-fastapi-mount

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Serve a built Vite/React SPA from FastAPI with asset mounting, catch-all routing, and path-traversal protection.

SKILL.md

2.6 KB, 485 tokens by cl100k_base, as published. Nobody here has run it

SPA fallback FastAPI mount

When to use

You have one FastAPI process that in some deployments (Docker, web mode) also serves the built SPA, and in other deployments (desktop sidecar, API-only) does not ship the SPA. The mount must be a no-op when the frontend directory is absent.

Steps

  1. Detect the built frontend at startup (e.g. Path(__file__).resolve().parent.parent / "frontend"). If frontend_dir.is_dir() is false, return — keep API-only deployments clean.
  2. Mount hashed assets from frontend/assets/ under /assets using StaticFiles. Vite emits content-hashed filenames here, so long-term caching is safe.
  3. Register a catch-all route last: @app.get("/{full_path:path}"). Because FastAPI resolves routes in registration order, all API routes must be added before this — register it only from inside _mount_frontend invoked after register_routers(app).
  4. Inside the catch-all, resolve the requested path under the frontend directory (file_path = (frontend_dir / full_path).resolve()). Guard with file_path.is_relative_to(frontend_dir) to reject path-traversal (../../etc/passwd). If the resolved file exists and is inside the frontend dir, serve it; otherwise serve index.html so client-side routes like /voices, /settings work.
  5. Always return index.html with media_type="text/html" on the fallback — some static file helpers default to application/octet-stream for unrecognized extensions.

Counter / Caveats

  • If you mount before registering API routers, the catch-all will swallow /api/... requests. Always mount last.
  • is_relative_to is only available in Python 3.9+. If you need broader support, compare resolved.parts[: len(root.parts)] == root.parts.
  • Do not set html=True on StaticFiles for the whole frontend — it conflicts with the explicit catch-all and hides 404s.
  • When the frontend emits new assets, old hashed files remain in cache but index.html does not — make sure index.html is served with short TTL (via your reverse proxy) so clients pick up new builds.

Source references: backend/app.py (_mount_frontend).

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.