Split Architecture: UI on Vercel + Backend on Render
User -> Vercel (FastHTML + Faststrap, stateless, use_cdn=True)
|
| hx_post="https://api.yourapp.com/..."
v
Render / Railway (FastAPI backend, persistent)
|
|-- PostgreSQL (Supabase / Render DB)
|-- SSE endpoints
`-- Background workers
Frontend (Vercel) - main.py
from fasthtml.common import *
from faststrap import add_bootstrap, Container, Input, Div
add_bootstrap(app, use_cdn=True)
@app.route("/")
def home():
return Container(
Input(
"search",
placeholder="Search...",
hx_get="https://api.yourapp.com/search",
hx_target="#results",
hx_trigger="keyup changed delay:500ms",
),
Div(id="results"),
)
Backend (Render/Railway) - FastAPI CORS
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourapp.vercel.app"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=[
"HX-Location", "HX-Redirect", "HX-Refresh",
"HX-Trigger", "HX-Reswap", "HX-Retarget",
],
)
HX-* headers must be explicitly exposed
CORS blocks custom response headers by default. Without expose_headers,
Faststrap preset response helpers (toast_response(), hx_redirect(), etc.)
silently fail cross-origin.
Database Options
| Option | Cost | Best For |
|---|---|---|
| Supabase | Free tier | Easy setup, HTTP API |
| Render PostgreSQL | Free tier | Already on Render |
| Neon | Free tier | PostgreSQL, serverless-native |