Offline-first clinic management backend for Open Source Kigali. Built with NestJS, TypeScript, Prisma, and PostgreSQL. Clinic devices work while disconnected and reconcile changes through a bidirectional sync protocol when they are back online.
TL;DR clone the repo and install dependencies, set up your
.envfile, start the database, and you are up. Read more below for environment variables, database setup, project structure, and scripts. Here are the quick commands to get started.
npm install
cp .env.example .env
docker compose up -d db
npx prisma migrate dev
npm run start:devThe server runs on http://localhost:3000 and Swagger UI is at http://localhost:3000/docs/api.
If a local PostgreSQL already uses port 5432, set POSTGRES_PORT to another value (for example 5433) in .env and update DATABASE_URL to match.
Environment variables
See .env.example for the full list.
| Variable | Required | Description |
|---|---|---|
PORT |
no | Server port (default 3000) |
NODE_ENV |
no | development or production |
DATABASE_URL |
yes | PostgreSQL connection string |
JWT_SECRET |
yes | Signs user login tokens; use a long random value |
JWT_EXPIRES_IN |
no | User token lifetime (default 1h) |
DEVICE_JWT_SECRET |
yes | Signs per-device sync tokens; separate from JWT_SECRET |
DEVICE_JWT_EXPIRES_IN |
no | Device token lifetime (default 30d) |
SYNC_PULL_MAX_LIMIT |
no | Max changes returned by one pull page (default 500) |
SYNC_FULL_RESYNC_THRESHOLD |
no | Cursor lag that forces a full re-sync (default 50000) |
THROTTLE_TTL_SECONDS |
no | Rate-limit window in seconds (default 60) |
THROTTLE_LIMIT |
no | Requests allowed per window (default 120) |
Database
PostgreSQL runs locally via Docker. Make sure Docker is installed, then:
docker compose up -d db # start Postgres
npx prisma migrate dev # apply migrations
npx prisma studio # optional: browse the DB in a GUI
npm run db:seed # optional: demo clinic and admin loginTo stop the database run docker compose down (add -v to wipe the data).
Project structure
src/
├── main.ts Bootstrap, Swagger, global pipes
├── app.module.ts Root module
├── config/ Typed environment configuration
├── prisma/ Prisma service and module
├── auth/ Login, JWT strategy, guards
├── patients/ Patients module
├── audit/ Audit log service
├── sync/ Sync layer (handshake, push, pull, entity syncers)
└── health/ Liveness endpoint
prisma/
├── schema.prisma Prisma schema
├── migrations/ Generated migration history
└── seed.ts Optional local seed
Scripts
npm run start:devstart the server in watch modenpm run buildcompile TypeScript todist/npm run start:prodrun the compiled buildnpm testrun the unit testsnpm run lintlint the codebasenpm run formatformat with Prettiernpm run prisma:migratecreate and apply a migrationnpm run db:seedseed a demo clinic and admin user
The interactive Swagger UI is available at http://localhost:3000/docs/api once the server is running.
Authentication uses two bearer tokens: a user JWT from login, and a per-device token issued at sync handshake. The sync surface is:
| Endpoint | Auth | Purpose |
|---|---|---|
POST /sync/handshake |
user JWT | Register a device and get a device token |
POST /sync/push |
device token | Send a batch of local changes (idempotent) |
GET /sync/pull |
device token | Read changes after a cursor, including tombstones |
Contributions are welcome. Please read CONTRIBUTING.md for setup, branching, commit conventions, and the pull request flow. By participating, you agree to follow our Code of Conduct. To report a bug or request a feature, open an issue using one of the templates in .github/ISSUE_TEMPLATE. To report a security issue, read SECURITY.md.
To add yourself, open a pull request that adds your GitHub username to CONTRIBUTORS.md.