Skip to Content
ContributeBackendOverview

Backend Documentation

This section is about the Rhesis backend: a FastAPI application that exposes REST APIs, handles authentication and organizations, and coordinates work with PostgreSQL, Redis, and Celery workers. The service is written for Python 3.12+ and uses uv for dependencies (see apps/backend/pyproject.toml).

Running the API locally

The smoothest path is the rh CLI from the repository root. From there, run the steps below in order. The first command creates apps/backend/.env and apps/frontend/.env.local with sensible defaults for local Postgres and Redis ports, JWT placeholders, and related settings.

local-api.sh
# One-time: environment files
./rh dev init

# Start Postgres and Redis
./rh dev up

# Install deps (uv sync), migrate if configured, then serve on port 8080
./rh dev backend

./rh dev backend runs uv sync, then apps/backend/start.sh, which calls migrate.sh when the database is configured, then starts Uvicorn on port 8080.

If you prefer not to use rh, go to apps/backend, ensure .env is present, run uv sync, then ./start.sh. Set SKIP_MIGRATIONS=true if something else already applied migrations (for example a Cloud Run migration job in CI).

Database migrations

All migration paths use apps/backend/migrate.sh, which runs alembic upgrade head:

  • Local devstart.sh loads .env, then calls migrate.sh (uses uv run alembic when needed).
  • Docker Composestart.sh in the backend container calls migrate.sh.
  • Cloud deploy — a separate Cloud Run Job runs migrate.sh; the backend service sets SKIP_MIGRATIONS=true.

In local and test environments (ENVIRONMENT or BACKEND_ENV is local or test), migrate.sh skips the Postgres wait loop and database ownership step. Deployed environments run both before applying migrations.

You can also run migrations directly: cd apps/backend && ./migrate.sh (with .env loaded or database variables exported).

Once the server is up, open interactive docs at http://localhost:8080/docs, or ReDoc and the OpenAPI JSON at /redoc and /openapi.json. A simple GET /health responds with {"status": "ok"}. Background processing is not started with the API alone; use ./rh dev worker from the repo root when you need Celery (details in Background tasks).

How the codebase is organized

HTTP routes live under app/routers/, with Pydantic types in app/schemas/ and SQLAlchemy models in app/models/. Shared database helpers sit in app/crud.py. Authentication and token handling are grouped under app/auth/ (see Authentication). Celery-related code is split between tasks/ (what runs in the worker), celery/ (app wiring), and worker.py at the package root. Database migrations are Alembic revisions under src/rhesis/backend/alembic/, applied by migrate.sh (see above).

A trimmed view of apps/backend:

layout.txt
apps/backend/
├── pyproject.toml
├── migrate.sh               # alembic upgrade head (local, Docker, Cloud Run Job)
├── start.sh                 # loads .env, calls migrate.sh, starts the server
└── src/rhesis/backend/
  ├── app/
  │   ├── main.py
  │   ├── routers/
  │   ├── models/
  │   ├── schemas/
  │   ├── services/
  │   ├── auth/
  │   ├── utils/
  │   ├── database.py
  │   └── crud.py
  ├── alembic/
  ├── tasks/
  ├── celery/
  ├── metrics/
  ├── logging/
  └── worker.py

Tests and code quality

Automated tests are not inside apps/backend; they live in tests/backend and tests/notifications, wired through pyproject.toml in the backend project. From apps/backend you can run the suite with:

test.sh
cd apps/backend
uv run pytest ../../tests/backend/ -v

Formatting and linting use Ruff. The project’s conventions and exact commands are described in Development workflow; the usual pattern is uv run --all-groups ruff check . and uv run --all-groups ruff format . from apps/backend.

Configuration

Secrets and connection strings are environment-driven. For a structured list of variables, see Environment configuration and the central Environment variables reference. Local ./rh dev init output is a good template for values such as APP_DB_PASS, Redis broker URLs, and JWT_SECRET_KEY.

Further reading