Static Site Generation
The Problem
The web application runs FastAPI with server-side template rendering and JSON API endpoints. GitHub Pages serves only static files — no Python, no server-side logic. We need to pre-generate every page and every API response as files.
The Approach
scripts/build_static.py uses FastAPI's TestClient to request every
page and API endpoint from the running application, then writes the responses to files:
HTML pages → _site/occupation/11-1011/index.html
API JSON → _site/api/occupations/11-1011.json
A JavaScript "fetch shim" is injected into every HTML page's <head>. It
intercepts fetch() calls to /api/ URLs and redirects them to the
corresponding .json files. The page's JavaScript doesn't know it's running on
a static site — it fetches data the same way it would from a live server.
How the Fetch Shim Works
Page JS calls: fetch('/api/occupations/11-1011/wages?geo_type=state')
Shim maps to: fetch('/api/occupations/11-1011/wages-state.json')
For search, the shim downloads the full occupation index once, caches it, and filters client-side — no server needed.
For geography comparison trends, the shim maps the soc_code query parameter
to a filename:
fetch('/api/trends/compare/geography?soc_code=11-1011')
→ fetch('/api/trends/compare/geography-11-1011.json')
The Limitation: Combinatorial Endpoints
The /api/trends/compare/occupations?soc_codes=11-1011,13-1011,15-1211 endpoint
accepts arbitrary combinations of SOC codes. With ~870 occupations, the number of possible
combinations is astronomical. You cannot pre-generate all of them.
Key insight: Static site generation is inherently limited for endpoints with combinatorial query parameters. The static site accepts graceful degradation here — comparison features have reduced functionality compared to the live server.
Path Rewriting for GitHub Pages
GitHub Pages serves this site at /jobclass/ (a subpath), not at the root.
Every absolute URL in the HTML (/api/, /static/,
/occupation/, /trends/) must be rewritten to include the base
path (/jobclass/api/, etc.). The rewrite_paths() function handles
this with string replacements before the shim is injected.
The shim itself does NOT need path rewriting — it extracts the base path from the
URL at runtime using u.indexOf('/api/').
Adding New Routes to the Static Site
Every new route or API endpoint in the web app must also be added to
build_static.py. The checklist:
- Add HTML page generation (the
write_html()calls) - Add API JSON generation (the
write_json()calls) - Add path rewriting entries for new URL prefixes
- Update the fetch shim if the endpoint uses query parameters that need filename mapping
- Rebuild and deploy