FastAPI Migration
Goal: Migrate to FastAPI for better performance, strong typing, data validation, and improved developer ergonomics. Status (Aug 2026): Most high-traffic endpoints migrated.
Local Development Ports
When developing and testing locally, be aware of which port serves which version:
| Port | Serves | Notes |
|---|---|---|
localhost:8080 | FastAPI | Primary entry point in local dev. Serves all FastAPI endpoints; unmatched requests are proxied to web.py via openlibrary/fastapi/proxy.py (added in #13423) |
localhost:18080 | — | No longer used in local dev. FastAPI still runs on 18080 in production/staging, behind the site's proxy |
To test your new FastAPI endpoint:
- Swagger UI: Visit http://localhost:8080/docs to interact with and test your endpoint payloads.
- Direct testing: Hit
http://localhost:8080/<your-endpoint>directly with curl or your API client.
There is no DEPRECATED_PATHS list to maintain anymore — the old deprecated_handler.py was deleted in #13423. The fallback proxy forwards any request FastAPI has no route for to web.py automatically.
Workflow
- Create: Build the new FastAPI endpoint, ensuring it matches existing functionality.
- Stage: Deploy to the testing environment (available at
_fast/<endpoint>). - Route (Testing): Update testing NGINX config to point to the new endpoint.
- Verify: Test edge cases, comparing testing traffic to production.
- Deploy: Merge to
master, updateolsystemrouting, and wait for deployment. - Cleanup: Remove old endpoints, add local redirects (
deprecated_urls), and merge.
Migrating an Endpoint
This is a short guide for migrating a remaining web.py endpoint to FastAPI.
1. Understand the Existing Endpoint
- Find the endpoint in the codebase and read it carefully.
- Identify all input parameters (path, query, body) and their types.
- Understand the output format (JSON shape, status codes, error cases).
- Note any authentication requirements (required login vs. optional user).
2. Implement in FastAPI
- Place the new endpoint in the appropriate file under
openlibrary/fastapi/. Stubs already exist for most areas that need migration. - Follow the patterns established in the existing FastAPI files. Key references:
- Auth: See
auth.pyfor how to require authentication or get an optional current user.public_my_books.pyis an example of an option user andyearly_reading_goals.pyis an example of a required user.get_current_user()is only needed if you want an infogami User object with special functions attached. Generally you won't need this unless it was already used.
- Validation: Use Pydantic models for request bodies and response shapes. Declare path/query parameter types explicitly so FastAPI validates them automatically.
- Business logic: Keep endpoints thin — extract logic into helpers or static methods rather than embedding it in the route handler.
- Auth: See
- Refer to the FastAPI skill and docs and existing endpoints in the codebase for best practices (e.g.,
Annotatedfor dependencies and path params).
3. Write a Comparison Test Script
Create a temporary bash script (e.g., test_<endpoint>_compare.sh) to verify the old and new endpoints behave identically:
- Call the same request against both:
- FastAPI (new) →
localhost:8080(FastAPI is now the local dev entry point) - web.py (legacy) → not exposed on a host port by default anymore; temporarily map it in
compose.override.yaml(e.g. addports: [ "8081:8080" ]to thewebservice) and uselocalhost:8081
- FastAPI (new) →
- Compare response bodies — they should be identical for success cases.
- Minor differences in status codes or validation error messages are acceptable.
- Cover the happy path and important edge cases.
#!/usr/bin/env bash
# Temporary comparison script — delete before merging.
ENDPOINT="/api/example.json?param=value"
NEW=$(curl -s "http://localhost:8080${ENDPOINT}")
OLD=$(curl -s "http://localhost:8081${ENDPOINT}")
if [ "$OLD" = "$NEW" ]; then
echo "✅ Outputs match"
else
echo "❌ Outputs differ"
diff <(echo "$OLD" | python3 -m json.tool) <(echo "$NEW" | python3 -m json.tool)
fiNote: This script is for local comparison only. We will delete it before merging.
4. Deprecate the Old Endpoint
Once the FastAPI endpoint is working, mark the old web.py endpoint as deprecated using the existing pattern:
Why separate PRs? We generally keep the old endpoint alive until the new one is verified in production. This way, if something breaks, we can quickly revert the routing to point back at the old web.py version without needing another PR. Only after the new endpoint has been stable in production do we delete the old one.
@deprecated("migrated to fastapi")Do not delete the old endpoint yet. Removal will happen in a separate PR once we are confident the new endpoint is stable in production.