Deploying#
This page covers the parts of a production deployment that are specific to FastAPI-Restly. Everything else — uvicorn workers, gunicorn, TLS, reverse proxies, Docker, behind-a-proxy headers — is already covered well in FastAPI’s deployment docs and is not duplicated here.
Database configuration#
Drive the engine from environment variables. A small pydantic-settings
shim keeps the wiring obvious and 12-factor friendly:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
db_pool_size: int = 5
db_max_overflow: int = 10
settings = Settings() # reads DATABASE_URL etc. from the environment
Pass those values into fr.configure() via an explicit engine so you can
set pool options:
import fastapi_restly as fr
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(
settings.database_url,
pool_size=settings.db_pool_size,
max_overflow=settings.db_max_overflow,
pool_pre_ping=True,
)
fr.configure(async_engine=engine)
pool_pre_ping=True is recommended in production: it issues a lightweight
liveness check before handing out a pooled connection, which prevents
stale-connection errors after database restarts or network blips.
Migrations with Alembic#
In production, never call metadata.create_all() — use Alembic. Initialise
once in your project root:
alembic init alembic
Point alembic/env.py at the metadata of whichever declarative base your
models inherit from (typically fr.DataclassBase):
# alembic/env.py
import fastapi_restly as fr
import myapp.models # noqa: F401 — import side-effect: registers model classes
target_metadata = fr.DataclassBase.metadata
Run migrations as part of your release / startup pipeline:
alembic upgrade head
If you want tests to exercise the same migration path, add a project-local
fixture that runs alembic upgrade head before the suite. Restly’s pytest
plugin does not run migrations automatically. See How-To: Testing.
A production main.py template#
from contextlib import asynccontextmanager
import fastapi_restly as fr
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
from .settings import settings
from .views import UserView
engine = create_async_engine(
settings.database_url,
pool_size=settings.db_pool_size,
max_overflow=settings.db_max_overflow,
pool_pre_ping=True,
)
@asynccontextmanager
async def lifespan(_app: FastAPI):
yield
await engine.dispose()
app = FastAPI(lifespan=lifespan)
fr.configure(app, async_engine=engine)
fr.include_view(app, UserView)
Notes:
fr.configure(app, ...)installs the default exception handlers (currently theIntegrityError → 409translator). Passinstall_default_exception_handlers=Falseto opt out.engine.dispose()inlifespancleans up the connection pool on shutdown so workers exit promptly.Register views deliberately during startup with
fr.include_view(app, ViewClass); avoid relying on import-time side effects in larger apps.
Running the app#
Use any production ASGI runner. The most common options are uvicorn and gunicorn with uvicorn workers. See FastAPI’s deployment docs for the full picture, including TLS, reverse proxies, and Docker.
A minimal example:
uvicorn myapp.main:app --host 0.0.0.0 --port 8000 --workers 4
Sync RestView endpoints run on FastAPI’s threadpool, so worker count
still has the usual effect; async AsyncRestView endpoints share the
event loop within a worker. Don’t use --reload in production.