Skip to main content

Backend

The backend is a FastAPI application using async SQLAlchemy and PostgreSQL. It owns authentication, authorization, persistence, AI/RAG orchestration, uploads, notifications, and all business rules that cannot be trusted to the client.

App Factory and Middleware

server/app/main.py creates the FastAPI app. It:

  • Registers exception handlers for domain errors, validation errors, integrity conflicts, and unknown exceptions.
  • Mounts all /api/* routers.
  • Serves public /uploads/*.
  • Serves the built SPA from web/dist in production.
  • Adds security headers and CORS.
  • Enables Langfuse/LangSmith setup during lifespan when configured.
  • Marks interrupted document ingests as failed on startup.

Route mounting order matters: campaign sub-resources are mounted before the base campaign router so longer paths win.

Router Pattern

Routers are grouped by domain in server/app/routers/.

Every route should:

  1. Authenticate with require_auth or require_admin.
  2. Authorize with require_member, require_dm, or domain-specific helpers.
  3. Validate request bodies with Pydantic schemas.
  4. Delegate non-trivial logic to server/app/lib/.
  5. Serialize ORM data through server/app/serializers.py.

Error Handling

Raise helpers from server/app/errors.py instead of returning ad-hoc error responses. The app factory converts those errors into a consistent JSON envelope.

Use:

  • badRequest for invalid input or impossible state transitions.
  • unauthorized for missing/invalid credentials.
  • forbidden for authenticated users lacking permission.
  • notFound for missing scoped resources.
  • conflict for state conflicts.

Business Logic

Keep routers thin. Shared or complex logic belongs under server/app/lib/.

Examples:

ModuleResponsibility
access.pyCampaign role checks, AI access checks, visible monster scope.
campaign_clone.pyCampaign duplication.
combat.pyCombat tracker mutations and encounter expansion.
encounter_xp.pyDeterministic encounter difficulty math.
encounter_suggest.pyAI encounter suggestion orchestration.
entities.pyCodex entity and recap-apply logic.
npcs.pyNPC helper logic and relationship cleanup.
uploads.pyPublic upload paths and validation.
rag/Document ingestion, retrieval, chat, generation, tracing.

Background Work

The app uses FastAPI BackgroundTasks for document ingestion, re-processing, email notifications, and recap extraction. Background tasks run in-process; they do not survive a server restart. Ingestion recovery in rag/ingest.py marks stuck work as failed at startup so the UI can offer a retry.

Any blocking model or file-processing work inside async flows should run through asyncio.to_thread so the event loop remains responsive.