Thanks for contributing. RELab is a research platform developed at CML, Leiden University. The goal of this document is simple: get you productive without making you dig through the repo first.
This page is for code and documentation changes. If you mainly want to run or deploy the stack, see Install & Self-Host.
| I want to... | Start here |
|---|---|
| get the recommended working environment | Devcontainer Setup |
| run the full stack locally in Docker | Docker Development |
| work on one subrepo directly | Local Development |
| understand the system first | docs.cml-relab.org/architecture |
| understand config ownership | engineering configuration |
By participating, you agree to follow the Code of Conduct.
This is the recommended path into the repo if you use VS Code.
-
Clone the repository.
-
Create the environment files.
cp backend/.env.dev.example backend/.env.dev cp .env.example .env
-
Fill in the required values in
backend/.env.devand.env. -
Reopen the repo in the
relab-fullstackdevcontainer. -
Run the standard bootstrap path.
just setup just dev
-
Run the standard checks when you want to verify the repo state.
just ci
| Configuration | Purpose |
|---|---|
relab-fullstack |
primary onboarding path for full stack development |
relab-backend |
focused backend work |
relab-frontend-app |
focused Expo app work |
relab-frontend-web |
focused public site work |
relab-docs |
focused docs work |
- Platform: http://127.0.0.1:8010
- Backend: http://127.0.0.1:8011
- Docs: http://127.0.0.1:8012
- App frontend: http://127.0.0.1:8013
- PostgreSQL:
5432 - Redis:
6379
Use this when you want the full stack without configuring each subrepo manually.
-
Create the backend environment file.
cp backend/.env.dev.example backend/.env.dev cp .env.example .env
-
Install local tooling.
just setup
-
Start the stack with file watching.
just dev
-
Run migrations on first start.
just dev-migrate
- Platform: http://127.0.0.1:8010
- Backend: http://127.0.0.1:8011
- Docs: http://127.0.0.1:8012
- App frontend: http://127.0.0.1:8013
just dev-up # start without file watching
just dev-logs # tail logs
just dev-down # stop containersUse this when you want to work on a specific subrepo without Docker.
Install:
Then run:
git clone https://github.com/CMLPlatform/relab
cd relab
just setupThe repo uses just as the common task runner.
From the repo root:
just setup
just ci
just test
just test-integration
just securityUse just --list in any directory to see what is available there.
Manifest ownership, env rules, and infra review guidelines live in the engineering configuration docs.
The backend lives in backend/.
uv- PostgreSQL
- Redis recommended; required for production-like auth behavior
cd backend
uv sync --all-groups --frozen
cp .env.dev.example .env.dev
./scripts/local_setup.sh
just devThe API is available at http://127.0.0.1:8000.
- Public docs: http://127.0.0.1:8000/docs
- Full docs: http://127.0.0.1:8000/docs/full after authenticating as a superuser
Keep examples centralized and predictable:
- Domain-specific examples go in
examples.py(e.g.,backend/app/api/data_collection/examples.py) - Cross-domain examples go in
backend/app/api/common/openapi_examples.py - Use
*_EXAMPLEfor single payloads,*_EXAMPLESfor schema lists,*_OPENAPI_EXAMPLESfor FastAPI named maps - In routers, pass examples via
openapi_examples=...parameter - Update
backend/tests/integration/api/test_openapi_endpoints.pywhen changing examples
Keep modules small, explicit, and domain-shaped:
- One top-level package per domain:
auth,background_data,data_collection,file_storage,newsletter,plugins/rpi_cam - Prefer flat modules first:
crud.py,dependencies.py,examples.py,exceptions.py,filters.py,models.py,schemas.py - Use
routers/only when multiple route files exist; entrypoint goes inrouters/__init__.py - Use
models/only when both ORM models and storage primitives exist; expose public surface atmodels/__init__.py - Use
services/andutils/only when they reflect a real boundary; delete pass-through layers when simple enough to call directly - Keep shared behavior in
backend/app/api/common/
Keep the backend suite organized by execution cost first, then by feature:
backend/tests/unit/: pure unit tests only, with no database session, testcontainers startup, or real app lifespanbackend/tests/integration/db/: CRUD, ORM, and persistence behavior against the real schemabackend/tests/integration/api/: HTTP endpoint behavior against the ASGI appbackend/tests/integration/flows/: a small set of cross-boundary, multi-step scenarios
Use the backend test commands that match those tiers:
cd backend
just test-unit
just test-integration-db
just test-api
just test-flows
just test-ciFixture conventions should stay explicit and descriptive:
db_sessionfor database accessdb_useranddb_superuserfor persisted auth principalsapi_client,api_client_user, andapi_client_superuserfor HTTP testsredis_clientor feature-local Redis fixtures where applicable
Do not add or reintroduce session or superuser as in-repo fixture aliases. Use the canonical db_session and db_superuser names directly.
Do not add compatibility-only test coverage for fixture aliases, re-export modules, or pass-through wrappers unless they protect a deliberate stable external contract. Prefer testing behavior at the canonical fixture or module surface.
Keep fixtures close to the tests that use them when the reuse is local. Reserve backend/tests/conftest.py for bootstrap concerns such as testcontainers, test database setup, and global logging behavior. Avoid broad autouse fixtures unless they are true cross-suite safety rails.
Keep API tests focused on one behavior per test. Avoid multi-step CRUD journeys in tests/integration/api/; move those broader stories to tests/integration/flows/.
Path is the primary source of truth for where a test belongs:
- Choose
unitwhen the test can run with mocks/stubs only. - Choose
integration/dbwhen the behavior depends on SQLAlchemy queries, migrations, or constraints. - Choose
integration/apiwhen the behavior is expressed as HTTP requests against the app. - Choose
flowsonly when the value comes from verifying a full multi-step journey.
If a test file starts growing into a mixed “god file”, split it by behavior before adding more cases.
cd frontend-app
pnpm install --frozen-lockfile
just devThe Expo dev server runs on http://localhost:8081.
If you are using a physical device or a non-default backend URL, create frontend-app/.env.local and set EXPO_PUBLIC_API_URL.
To enable Google OAuth on web, set EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID in your env file to the web client ID from Google Cloud Console. The authorized redirect URI for your environment must also be registered there (e.g. http://localhost:8013/login for local dev).
The frontend TypeScript API types are autogenerated from the backend OpenAPI schema and written to frontend-app/src/types/api.generated.ts.
When working on backend API changes, regenerate the types:
# from repo root
cd frontend-app
pnpm run codegen:api
# regenerate and redact embedded JWT examples (recommended)
pnpm run codegenYou can also run just codegen inside frontend-app (after just install) which runs the regeneration and redaction steps.
cd frontend-web
pnpm install --frozen-lockfile
just devThe Astro dev server runs on http://localhost:8081.
cd docs
pnpm install --frozen-lockfile
just devThe docs site runs on http://localhost:8000.
The docs app is the canonical home for public guides, architecture reference, and project context. Keep repo-level setup text in this file short and link back to the docs site when deeper explanation belongs there.
If you are new to the repo, start with the architecture docs before making structural changes.
-
Create a branch.
git checkout -b feature/your-change
-
Make the change.
-
Run the relevant checks.
just ci
-
Push your branch and open a PR.
-
Address review feedback.
Use Conventional Commits:
<type>(<scope>): <short summary>
The repo uses GitHub Actions for:
- normal CI
- security checks
- release automation
Locally, the important commands are:
just cijust testjust test-integrationjust security
The backend uses:
- Ruff for linting and formatting
- Ty for static type checking
- ShellCheck for shell scripts
Useful commands from backend/:
just lint
just format
just fix
just typecheck
just shellcheck
just checkUseful commands from backend/:
just test
just test-unit
just test-integration
just test-covWhen adding backend behavior, add tests close to the change. Prefer small unit tests unless the behavior really depends on routing, persistence, or integration boundaries.
When changing schema:
-
Create a migration.
cd backend just migrate-create "describe the change"
-
Review the generated file in
alembic/versions/. -
Apply it.
just migrate
For Docker-based runs, you can also use just dev-migrate from the repo root.
MJML source templates live in backend/app/templates/emails/src/. Compiled HTML lives in backend/app/templates/emails/build/.
Do not edit compiled output directly.
To rebuild email templates:
cd backend
just compile-emailfrontend-appuses Expo linting and TypeScript-based toolingfrontend-webuses Biome and Astro validation- follow the existing folder structure and naming patterns
- prefer consistency with the current UI and component patterns over novelty
For frontend-app:
cd frontend-app
just test
just test-ci
just checkFor frontend-web:
cd frontend-web
just test
just test-ci
just test-e2e
just checkWhen adding a new public-facing page to frontend-web, add at least one browser test. When adding app behavior in frontend-app, add Jest coverage for the new logic or screen behavior.
- write plainly
- keep docs informative but simple
- avoid hype, filler, and brittle implementation detail unless it is genuinely needed
- prefer Markdown and Mermaid over custom HTML where possible
Before opening a docs-focused PR:
cd docs
just checkTo apply formatting:
cd docs
just formatBy contributing, you agree that your contributions are licensed under the project LICENSE.