Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contributing

First Time Setup

Prerequisites

ToolVersionFor
Ruststable, >= 1.95 (rust-toolchain.toml)aAPI, eDd server, eDb, dApp backend
Bun1.4.2 (CI)wApp, eDd client, dApp frontend
Dockerwith ComposeeDb, images
sqlx-cli0.9.0eDb migrations, .sqlx/
Tauri prerequisitesLinux: webkit2gtk-4.1, libsoup-3dApp only
mdbook, mdbook-mermaid0.5.4, 0.17.1 (CI)docs only
# For working with database stuffs
cargo install sqlx-cli --version 0.9.0 --locked --no-default-features --features rustls,postgres,sqlx-toml
# For working on docs/
cargo install mdbook@0.5.4 mdbook-mermaid@0.17.1 --locked # optional

Clone

git clone https://github.com/freeCodeCamp/exam-platform.git
cd exam-platform
bun install
docker network create platform

bun install also installs the Husky pre-commit hook.

Environment

Copy every example, then fill blanks:

for d in apps/auth-api apps/examiner-dashboard apps/web-app apps/desktop-app databases/examiner; do
  cp -n "$d/.env.example" "$d/.env"
done

.env is read automatically only by:

Reads .env automaticallyDoes not
Bun (wApp, eDd client, dApp)cargo run, cargo test, cargo clippy
Vite (dApp frontend)rust-analyzer
docker compose (from its directory)
sqlx-cli (from its directory)

Rust apps read only the process environment. Export the app’s .env into the shell before cargo:

set -a; source apps/auth-api/.env; set +a
cargo run -p auth-api

Or use direnv: echo dotenv > .envrc && direnv allow in each app directory, then run cargo from there. .envrc is not in .gitignore; do not commit it.

FileSet
databases/examiner/.envEDB_PASSWORD, EDB_MIGRATOR_PASSWORD, EDB_EDD_PASSWORD, EDB_AAPI_PASSWORD, and the same in EDB_ADMIN_URL, EDB_MIGRATOR_URL
apps/auth-api/.envEDB_DATABASE_URL (as aapi, with EDB_AAPI_PASSWORD)
apps/examiner-dashboard/.envEDB_DATABASE_URL (as edd, with EDB_EDD_PASSWORD), COOKIE_KEY (openssl rand -hex 64), GOOGLE_OAUTH_ID, GOOGLE_OAUTH_SECRET (Auth)
OthersDefaults work for ENVIRONMENT=development. SENTRY_DSN stays empty

All variables: Environment Variables.

eDb

cd databases/examiner
docker compose up -d --wait
set -a; source .env; set +a
cargo run -p edb --bin edb-migrate

Run edb-migrate again after pulling new migrations. Reset with docker compose down -v.

Editor

VS Code: install recommended extensions (.vscode/extensions.json). Settings run clippy on save, set ENVIRONMENT=development for rust-analyzer, and link both Cargo workspaces.

Development

Run

AppCommandPort
aAPIcargo run -p auth-api127.0.0.1:13001
eDd servercargo run -p examiner-dashboard127.0.0.1:13002
eDd clientbun --filter examiner-dashboard-client dev127.0.0.1:13005
wAppbun --filter web-app dev127.0.0.1:13003
dAppbun run dev:desktoplocalhost:1420
eDbdocker compose up -d in databases/examiner/127.0.0.1:15432
Docsmdbook watch (or mdbook serve)localhost:3000

cargo run rows need their .env exported first (Environment). bun run dev starts wApp and eDd client together. eDd client proxies /api to eDd server. wApp and dApp call aAPI at AUTH_API_URL.

Images: docker compose -f apps/<app>/compose.yaml up --build. Build context is repo root.

Workspaces

WorkspaceManifestMembers
CargoCargo.tomlauth-api, examiner-dashboard, edb, libs/*
Cargoapps/desktop-app/Cargo.tomldApp backend. Separate so servers never resolve Tauri
Bunpackage.jsonweb-app, examiner-dashboard-client, desktop-app

Shared versions live in package.json catalogs: reference with catalog: or catalog:astro, do not pin in members.

Check

CI (.github/workflows/ci.yml) runs only jobs whose paths changed. Run the same locally before pushing:

# Root Cargo workspace (eDb running; source databases/examiner/.env for EDB_ADMIN_URL)
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked
cargo clippy -p runtime --no-default-features --all-targets --locked
DATABASE_URL=$EDB_ADMIN_URL cargo test --workspace --locked

# dApp backend
cargo fmt --all --check --manifest-path apps/desktop-app/Cargo.toml
cargo clippy --workspace --all-targets --locked --manifest-path apps/desktop-app/Cargo.toml
cargo test --workspace --locked --manifest-path apps/desktop-app/Cargo.toml

# Bun
bun run check
bun run build

# Deploy Action (migration gate, rollback)
(cd deploy && bun test)

Clippy all is deny. Format Rust with rustfmt (rustfmt.toml), eDd client with bun --filter examiner-dashboard-client fmt (oxfmt).

Database Changes

eDb schema, migrations and queries live in crate edb (databases/examiner/). Run every step from that directory with eDb up (docker compose up -d --wait). Only add, release, then remove: drop or rename only once no running or previous eDd or aAPI image uses the object. Never edit an applied migration.

sqlx-cli reads EDB_MIGRATOR_URL from its .env; cargo does not, so export it first.

  1. Create a reversible migration pair:
cargo sqlx migrate add -r -s <name> # migrations/NNNN_<name>.{up,down}.sql
  1. Write up and down SQL, naming the schema (edd.users; shared if both apps write the table, see eDb schemas), and grant each app role what it needs on new tables. down is for local development only; production never runs it.
  2. Apply it to local eDb:
cargo run -p edb --bin edb-migrate # or: cargo sqlx migrate run

To iterate on uncommitted migration: cargo sqlx migrate revert, make edits, then run it again.

  1. Add or change queries in src/edd/, src/aapi/ or src/shared/ (one module per table group, each an impl Database block of query!/query_as!). SQLX_OFFLINE is true in .cargo/config.toml, so cargo check -p edb checks them against committed .sqlx/, and fails on a new or changed query. To validate against local eDb, override it: SQLX_OFFLINE=false cargo check -p edb.
  2. Test with #[sqlx::test(migrations = false)], calling testing::setup first: it bootstraps roles and schemas, then migrates as edb_migrator. Query through testing::database_as to run as the app’s role. #[sqlx::test] reads DATABASE_URL, not sqlx.toml, and creates a database per test:
DATABASE_URL=$EDB_ADMIN_URL cargo test -p edb
  1. Refresh offline query data:
cargo sqlx prepare

The Husky pre-commit hook runs this and stages .sqlx/ when eDb files are staged. This step never blocks the commit: if it warns with cargo sqlx prepare failed; .sqlx/ may be stale, manually run step 6.

  1. Commit migration, queries and .sqlx/ together. Local (.cargo/config.toml), Docker and CI builds all use SQLX_OFFLINE=true. CI fails on a stale .sqlx/: offline clippy on a missing query, cargo sqlx prepare --check on one whose types changed.

Commits and PRs

Conventional Commits: release-please reads them to version each app (Releases).

<type>(<scope>): <summary>
PartValues
typefeat, fix, docs, chore, dev, refactor. feat and fix trigger release; others do not
scopeApp or area: aAPI, eDd, eDb, wApp, dApp, docs, .github. Omit when repo-wide

Follow Database and Rust conventions.