Handsoff is a NestJS backend for clinical operations: patient records, appointments, pharmacy, laboratory, analytics, and integrations.
- Project Structure
- Local Development with Docker
- Background Worker Process
- Getting Started
- Configuration
- Scripts Reference
- Authentication & Authorization
- Security Headers
- API Endpoints
- Webhooks
- Postman Collection
- Database Schema
- Multi-Tenancy
- Error Handling
- Testing
- Deployment
| File | Purpose |
|---|---|
src/main.ts |
HTTP API process — bootstraps the NestJS app, applies global middleware, starts listening |
src/worker.ts |
BullMQ worker process — runs queue processors without exposing an HTTP port |
src/worker.module.ts |
NestJS module that wires only the queue processors (no HTTP layer) |
packages/sdk/ |
Auto-generated TypeScript client SDK published to npm |
Modules are grouped below by domain. Each entry is a top-level directory under src/.
| Directory | Description |
|---|---|
patients/ |
Patient registry, demographics, geo-restrictions, notification preferences |
medical-records/ |
Core medical records with version control, consent, clinical notes, attachments |
records/ |
Versioned record storage with IPFS anchoring and Stellar event-sourcing |
pharmacy/ |
Drugs, prescriptions, drug interactions, recalls, inventory, controlled substances |
laboratory/ |
Lab orders, results, specimens, equipment, quality control |
appointments/ |
Scheduling, consultations, doctor availability |
diagnosis/ |
Diagnosis management and ICD code handling |
treatment-planning/ |
Care plans, clinical guidelines, decision-support alerts |
medication-administration/ |
MAR (Medication Administration Record), barcode verification, adverse reactions |
infection-control/ |
Outbreak tracking, isolation protocols, antibiotic resistance surveillance |
emergency-operations/ |
Triage, rapid-response teams, critical care coordination |
emergency-medical-info/ |
Break-glass emergency access to patient data |
pathology/ |
Histology, cytology, genetic testing, digital pathology |
hospital-registry/ |
Hospital and facility configuration |
| Directory | Description |
|---|---|
billing/ |
Claims, invoices, insurance, payment processing |
| Directory | Description |
|---|---|
stellar/ |
Stellar Horizon / Soroban SDK wrapper, transaction retry/recovery |
stellar-stream/ |
Real-time Stellar event streaming and re-indexing |
ledger-reconciliation/ |
Detects discrepancies between the DB and on-chain state |
reconciliation/ |
General data reconciliation jobs |
blockchain/ |
Low-level blockchain utilities and abstract contract interface |
event-store/ |
Append-only event-sourcing store for domain aggregates |
projections/ |
CQRS read-model projections built from the event store |
| Directory | Description |
|---|---|
app.module.ts |
Root module — wires all application modules together |
common/ |
Shared DTOs, interceptors, filters, guards, audit, pagination, throttler, circuit-breaker |
config/ |
Database config, env validation schema, logger config |
auth/ |
JWT, MFA, API keys, session management, OIDC/OAuth2 |
OAuth2/ |
OIDC provider module (wraps the auth OIDC flow) |
rbac/ |
Role-Based Access Control policy engine |
roles/ |
Medical RBAC decorators and guards (MedicalRole enum, MedicalRbacGuard) |
access-control/ |
Fine-grained access grants, revocations, and consent enforcement |
tenant/ |
Multi-tenancy — tenant resolution, Row-Level Security (RLS), tenant context |
tenant-config/ |
Per-tenant feature flags and configuration overrides |
notifications/ |
WebSocket gateway, transactional outbox, email, notification preferences |
queues/ |
BullMQ job queues, DLQ, queue dashboard |
graphql/ |
Apollo GraphQL server, subscriptions, dataloaders, cursor pagination |
pubsub/ |
GraphQL PubSub transport (Redis-backed) |
subscriptions/ |
GraphQL subscription lifecycle management |
metrics/ |
Prometheus metrics, Grafana dashboards, SLO tracking |
health/ |
/health endpoint (Terminus health indicators) |
analytics/ |
Admin statistics and platform-level activity tracking |
jobs/ |
Scheduled background jobs (cron-based) |
data-retention/ |
Automated data-retention policy enforcement |
webhooks/ |
Outbound webhook delivery, signature verification |
idempotency/ |
Idempotency-key middleware for safe request replays |
dlq/ |
Dead-letter queue inspection and replay |
incident/ |
Incident tracking and escalation |
operator-runbook/ |
Runbook endpoints for on-call operators |
versioning/ |
API versioning helpers and deprecation interceptor |
admin/ |
Admin-only endpoints, user management, system configuration |
security/ |
Security headers config, IP allowlist, brute-force detection |
key-management/ |
AWS KMS / local envelope encryption, DEK rotation |
encryption/ |
PHI field-level encryption (AES-GCM, deterministic column transforms) |
i18n/ |
Internationalisation (nestjs-i18n), translation files, i18n exception filter |
feature-flags/ |
Runtime feature-flag evaluation |
circuit-breaker/ (under common/) |
Cockatiel-based circuit breaker for external calls |
| Directory | Description |
|---|---|
gdpr/ |
GDPR data-subject requests (access, erasure, portability) |
data-residency/ |
Geo-based data residency enforcement |
fhir/ |
FHIR R4 resource mapping and bulk export |
research-export/ |
De-identified, k-anonymous data exports for research |
ehr-import/ |
Structured import of external EHR data (HL7, CSV) |
governance-analytics/ |
Compliance dashboards and governance metrics |
consistency-checker/ |
Cross-system data consistency validation |
The following directories are named after GitHub issues rather than their domain, which makes the codebase harder to navigate. They contain valid implementations but should be renamed or merged in a future clean-up PR.
| Directory | Status | Recommended action |
|---|---|---|
src/Auto-Generate TypeScript Client SDK from OpenAPI Spec/ |
Duplicate — CI workflows only | Move workflows to .github/workflows/; delete directory |
src/SwaggerOpenAPI Documentation with Full Schema Coverage/ |
Contains export-openapi.ts |
Move script to scripts/; delete directory |
src/Profile and Optimize Database Query Performance Under Load/ |
Contains benchmark scripts | Merge into scripts/; delete directory |
src/profile-and-optimize-database-query-performance-under-load/ |
Duplicate of above (different casing) | Delete after merging |
src/Dockerfile and Docker Compose for Production-Ready Containerization/ |
Contains CI yml files | Move to .github/workflows/; delete directory |
src/Build Admin Analytics Dashboard Endpoints/ |
Unclear overlap with analytics/ |
Audit and merge or delete |
src/Telemedicine and Remote/ |
Contains spaces in name; likely overlaps with… | Merge into telemedicine-and-remote/ and delete |
src/telemedicine-and-remote/ |
…this directory | Keep this one; delete the spaced variant |
src/Tenant Provisioning and Onboarding Workflow/ |
Overlaps with… | Merge into tenant-provisioning-and-onboarding-workflow/ |
src/tenant-provisioning-and-onboarding-workflow/ |
…this directory | Keep; delete the spaced variant |
src/Department and Ward Management/ |
Unclear if wired into app.module | Audit and either wire or delete |
src/Surgical Management System/ |
Unclear if wired into app.module | Audit and either wire or delete |
src/Email Notification Service for Critical Access Events/ |
Likely superseded by notifications/ |
Audit and delete if redundant |
src/Hospital config/ |
Likely superseded by hospital-registry/ |
Audit and merge or delete |
src/Migration-CLI/ |
Likely superseded by TypeORM CLI scripts | Audit and delete if redundant |
src/modules/ |
Contains a patient sub-module alongside src/patients/ |
Audit which is canonical; delete the other |
Track clean-up work in a dedicated issue. Do not import from the spaced-name directories in new code.
| Service | Container | Port(s) | Purpose |
|---|---|---|---|
| api | hs-api | 3000 | NestJS app with hot reload |
| worker | hs-worker | - | BullMQ queue processors |
| postgres | hs-postgres | 5432 | PostgreSQL 15 |
| redis | hs-redis | 6379 | Redis 7 |
| mailhog | hs-mailhog | 1025 (SMTP), 8025 (UI) | Local email capture |
cp .env.docker .env.docker.local
docker compose -f docker-compose.local.yml up --build
docker compose -f docker-compose.local.yml exec api npm run migration:run
docker compose -f docker-compose.local.yml exec api npm run seed- API: http://localhost:3000
- Swagger: http://localhost:3000/api
- MailHog: http://localhost:8025
The src/ directory is bind-mounted; NestJS runs with --watch so changes reload automatically.
docker compose -f docker-compose.local.yml logs -f api # Follow API logs
docker compose -f docker-compose.local.yml logs -f worker # Follow Worker logs
docker compose -f docker-compose.local.yml down # keep volumes
docker compose -f docker-compose.local.yml down -v # wipe volumes
.env.dockercontains placeholder secrets for local use only. Never use outside local dev.
The backend application uses BullMQ for managing and executing background queues. This is essential for offloading heavy operations or interacting with the Stellar blockchain without blocking the main HTTP event loop of the API service.
The worker process is defined in src/worker.ts and src/worker.module.ts.
For how
src/blockchain/,src/stellar/, andsrc/stellar-stream/fit together and interact with this worker process — including which parts are fully wired up versus built-but-not-yet-connected — seedocs/STELLAR_ARCHITECTURE.md.
The worker processes tasks from several queues:
contract-writes: Schedules and signs write transactions to Stellar Soroban smart contracts.stellar-transactions: Manages blockchain transaction submission and retry logic.event-indexing: Scrapes and indexes events emitted by the smart contracts.ipfs-uploads: Uploads records/PHI hashes to IPFS.email-notifications: Sends transactional and notification emails.reports: Processes PDF/CSV healthcare compliance report generation.fhir-bulk-export/ehr-import: Processes FHIR/EHR batch data imports and exports.
The worker is included in docker-compose.local.yml and runs automatically when you spin up the project:
docker compose -f docker-compose.local.yml up --buildIf you run the NestJS API locally using npm run start:dev, you MUST also start the worker process in a separate terminal:
npm run start:worker:devThe application will be available at http://localhost:3000.
Swagger documentation will be available at http://localhost:3000/api.
Prerequisites: Node.js v18+ and PostgreSQL v12+.
- Install dependencies with
npm install. - Copy
.env.exampleto.envand configure the required values. - Run database migrations with
npm run migration:run. - Start the API with
npm run start:dev. - In a separate terminal, start the background worker with
npm run start:worker:dev.
Run npm run seed only when you want to populate a development database with sample data.
The project has 66 npm scripts. A full reference — including descriptions, prerequisites, and safety warnings for destructive scripts — is in docs/scripts.md.
Quick-start summary:
npm run start:dev # API with hot-reload
npm run migration:run # apply pending DB migrations
npm run seed # load development reference data
npm run test # unit tests
npm run test:e2e # end-to-end tests (requires Docker)
npm run load-test:ci # k6 load test + CI gate (requires k6)
npm run build:sdk # build the TypeScript client SDKCopy .env.example to .env and fill in the values:
cp .env.example .env.env.example is the authoritative reference for every environment variable the application reads. Variables are grouped by area and annotated with:
[REQUIRED]— must be set before the app starts in production[OPTIONAL]— has a safe default; override only when needed[SECRET]— never commit the real value; use a secrets manager in production
Key sections in .env.example:
| Section | Variables |
|---|---|
| Application | NODE_ENV, PORT, API_URL, APP_BASE_URL |
| Database (primary) | DATABASE_URL / DB_HOST … DB_POOL_MAX |
| Database (read replica) | DB_REPLICA_HOST … DB_REPLICA_POOL_MAX |
| Redis | REDIS_URL / REDIS_HOST … REDIS_DB |
| JWT & Auth | JWT_SECRET, JWT_EXPIRATION, JWT_REFRESH_SECRET, JWT_REFRESH_EXPIRATION |
| Encryption / KMS | ENCRYPTION_MASTER_KEY, MASTER_KEY, KMS_ENABLED, AWS_* |
| Stellar / Soroban | STELLAR_SECRET_KEY, STELLAR_CONTRACT_ID, SOROBAN_RPC_URL |
| IPFS | IPFS_NODE_URL, IPFS_GATEWAY, IPFS_FALLBACK_GATEWAY |
| Email / SMTP | SMTP_HOST, SMTP_USER, SMTP_PASS, MAIL_FROM_ADDRESS |
| Telemedicine | TELEMEDICINE_SIGNALING_URL, TELEMEDICINE_TOKEN_SECRET |
| Research export | RESEARCH_EXPORT_BUCKET, RESEARCH_K_ANONYMITY |
| EHR import | EHR_IMPORT_S3_BUCKET, EHR_IMPORT_MAX_RETRIES |
| GraphQL subscriptions | SUBSCRIPTIONS_IDLE_TIMEOUT_MS, SUBSCRIPTIONS_MAX_PER_CONNECTION |
| Ops / alerting | OPS_SLACK_WEBHOOK_URL, QUEUE_DEPTH_THRESHOLD |
| Migrations | MIGRATION_EXECUTOR, CONFIRM_PRODUCTION_MIGRATION |
The API applies the shared Helmet configuration once through applySecurityHeaders() in src/security/http-security.config.ts.
Prerequisites: Node.js v18+, PostgreSQL v12+
- Install dependencies:
npm install - Set up environment variables:
cp .env.example .env - Run database migrations:
npm run migration:run - Seed the database with test data:
npm run seed(Why added: This generates fake users, medical records, and access grants viasrc/database/seeder.tsso you can log in and test the application). - Start the development server:
npm run start:dev - Start the background job worker process:
npm run start:worker:dev
Copy .env.example to .env. Key sections:
The backend supports region-aware tenant database routing across four regions: EU, US, APAC, and AFRICA. Each tenant declares a residency region and, when strictDataResidency is enabled, requests are rejected with 403 Forbidden if they attempt to access data outside the configured region.
Each region has its own set of environment variables for database, Stellar Horizon, and IPFS configuration:
| Variable | Region | Description | Default |
|---|---|---|---|
DEFAULT_REGION |
global | Default region for new tenants | EU |
DB_TYPE_EU |
EU | Database type | postgres |
DB_HOST_EU |
EU | Database host | — |
DB_PORT_EU |
EU | Database port | 5432 |
DB_NAME_EU |
EU | Database name | healthy_stellar_eu |
EU_DB_URL |
EU | Database connection URL (overrides individual params) | — |
DB_URL_EU |
EU | Fallback database URL | — |
STELLAR_HORIZON_EU_URL |
EU | Stellar Horizon endpoint | https://horizon.eu.stellar.org |
IPFS_NODES_EU |
EU | Comma-separated IPFS node URLs | https://ipfs-eu-1.infura.io:5001 |
DB_TYPE_US |
US | Database type | postgres |
DB_HOST_US |
US | Database host | — |
DB_PORT_US |
US | Database port | 5432 |
DB_NAME_US |
US | Database name | healthy_stellar_us |
US_DB_URL |
US | Database connection URL (overrides individual params) | — |
DB_URL_US |
US | Fallback database URL | — |
STELLAR_HORIZON_US_URL |
US | Stellar Horizon endpoint | https://horizon.us.stellar.org |
IPFS_NODES_US |
US | Comma-separated IPFS node URLs | https://ipfs-us-1.infura.io:5001 |
DB_TYPE_APAC |
APAC | Database type | postgres |
DB_HOST_APAC |
APAC | Database host | — |
DB_PORT_APAC |
APAC | Database port | 5432 |
DB_NAME_APAC |
APAC | Database name | healthy_stellar_apac |
APAC_DB_URL |
APAC | Database connection URL (overrides individual params) | — |
DB_URL_APAC |
APAC | Fallback database URL | — |
STELLAR_HORIZON_APAC_URL |
APAC | Stellar Horizon endpoint | https://horizon.apac.stellar.org |
IPFS_NODES_APAC |
APAC | Comma-separated IPFS node URLs | https://ipfs-apac-1.infura.io:5001 |
DB_TYPE_AFRICA |
AFRICA | Database type | postgres |
DB_HOST_AFRICA |
AFRICA | Database host | — |
DB_PORT_AFRICA |
AFRICA | Database port | 5432 |
DB_NAME_AFRICA |
AFRICA | Database name | healthy_stellar_africa |
AFRICA_DB_URL |
AFRICA | Database connection URL (overrides individual params) | — |
DB_URL_AFRICA |
AFRICA | Fallback database URL | — |
STELLAR_HORIZON_AFRICA_URL |
AFRICA | Stellar Horizon endpoint | https://horizon.africa.stellar.org |
IPFS_NODES_AFRICA |
AFRICA | Comma-separated IPFS node URLs | https://ipfs-africa-1.infura.io:5001 |
Example configuration:
DEFAULT_REGION=EU
DB_HOST_EU=postgres-eu.internal.example.com
DB_PORT_EU=5432
DB_NAME_EU=healthy_stellar_eu
STELLAR_HORIZON_EU_URL=https://horizon.eu.stellar.org
IPFS_NODES_EU=https://ipfs-eu-1.infura.io:5001
DB_HOST_US=postgres-us.internal.example.com
DB_PORT_US=5432
DB_NAME_US=healthy_stellar_us
STELLAR_HORIZON_US_URL=https://horizon.us.stellar.org
IPFS_NODES_US=https://ipfs-us-1.infura.io:5001
DB_HOST_APAC=postgres-apac.internal.example.com
DB_PORT_APAC=5432
DB_NAME_APAC=healthy_stellar_apac
STELLAR_HORIZON_APAC_URL=https://horizon.apac.stellar.org
IPFS_NODES_APAC=https://ipfs-apac-1.infura.io:5001
DB_HOST_AFRICA=postgres-africa.internal.example.com
DB_PORT_AFRICA=5432
DB_NAME_AFRICA=healthy_stellar_africa
STELLAR_HORIZON_AFRICA_URL=https://horizon.africa.stellar.org
IPFS_NODES_AFRICA=https://ipfs-africa-1.infura.io:5001Tenant example:
{
"region": "EU",
"strictDataResidency": true
}When a policy violation occurs, the API returns:
403 Forbidden
Tenant data residency policy prohibits access outside the configured region.
For local development, the routing service initializes SQLite-backed regional datasources so tests and simulations can verify region selection without a full multi-database deployment.
| Section | Variables |
|---|---|
| Core | NODE_ENV, PORT, APP_URL, APP_DOMAIN |
| Database | DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_NAME |
| Encryption / PHI | ENCRYPTION_KEY, PHI_ENCRYPTION_KEY |
| JWT & Auth | JWT_SECRET, JWT_REFRESH_SECRET, JWT_REFRESH_EXPIRATION, SESSION_SECRET |
| CORS & Security | ALLOWED_ORIGINS, CORS_ORIGIN, ADMIN_IP_ALLOWLIST |
| Redis | REDIS_HOST, REDIS_PORT, REDIS_PASSWORD |
MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASSWORD |
|
| Stellar Blockchain | STELLAR_NETWORK, STELLAR_SECRET_KEY, STELLAR_CONTRACT_ID |
| Data Residency | DEFAULT_REGION, DB_TYPE_*, DB_HOST_*, DB_PORT_*, DB_NAME_*, *_DB_URL, STELLAR_HORIZON_*_URL, IPFS_NODES_* |
| IPFS | IPFS_HOST, IPFS_PORT, IPFS_URL |
| Webhooks | IPFS_WEBHOOK_SECRET, STELLAR_WEBHOOK_SECRET, QUEUE_HMAC_SECRET |
| OIDC / SSO | OIDC_PROVIDERS, OIDC_{PROVIDER}_CLIENT_ID, … |
| Logging | LOG_LEVEL, LOKI_HOST |
| Metrics & Tracing | METRICS_TOKEN, OTEL_EXPORTER_OTLP_ENDPOINT |
| Backup | BACKUP_DIR, BACKUP_ENCRYPTION_KEY, BACKUP_RETENTION_DAYS |
| Feature Flags | TELEMEDICINE_ENABLED, SURGICAL_MANAGEMENT_ENABLED |
Generate secrets with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"There is no global auth guard. Every controller that must be protected has to opt in explicitly with @UseGuards(JwtAuthGuard, ...); anything without a guard is public. Guards such as RolesGuard, AdminGuard, PatientPrivacyGuard and MedicalRbacGuard do not authenticate on their own, and decorators like @RequireAdmin() and @HipaaRoles() only attach metadata — they do nothing unless the matching guard runs.
The authenticated principal attached to the request is:
{ userId, email, role, mfaEnabled, sessionId, organizationId }There is no id, sub or tenantId field — use userId and organizationId. role is one of the UserRole enum values.
The full guide — token issuance and refresh, the guard catalogue (what each guard checks and what it depends on), the valid role list, @Public(), API keys, MFA, and SMART scopes — is in docs/auth.md.
Before opening a PR that adds or changes a controller, work through the new controller security checklist in CONTRIBUTING.md.
The API applies the shared Helmet configuration once through applySecurityHeaders() in src/security/http-security.config.ts:
Content-Security-Policy— defaults to same-origin resources, blocks object embedding and framing, and uses a per-request nonce for scripts. Development additionally allows the Swagger CDN.X-Frame-Options: DENY— blocks clickjacking through iframes.X-Content-Type-Options: nosniff— prevents MIME-type sniffing.Strict-Transport-Security— enables HTTPS-only access with a one-year max age, subdomains, and preload.Referrer-Policy: no-referrer— suppresses referrer information.X-XSS-Protection: 0— disables the legacy browser filter; CSP provides the script policy.
The complete route reference is available in the Swagger UI when the
application is running and in the generated OpenAPI specification.
The prefixes below summarize the API by domain; URI-versioned endpoints are
normally served under /v1 (version-neutral routes are not).
- Access control and roles:
/access,/users,/policies,/medical-rbac,/role-templates - Administration and operations:
/admin/*,/operator/runbooks - Analytics and governance:
/analytics/*,/governance/reports,/financial-reports - Appointments:
/appointments/*,/consultations,/doctor-availability - Authentication and identity:
/auth/*,/oauth2,/providers - Backup and recovery:
/backup - Beds, departments, and wards:
/bed-occupancy,/beds,/departments,/wards - Billing and insurance:
/billing/*,/claims,/denials,/appeals,/insurance,/medical-codes,/payments,/subscriptions - Clinical records:
/medical-records,/clinical-notes,/clinical-templates,/consents,/attachments - Clinical workflow:
/diagnosis,/treatment-plans,/procedures,/care-templates,/outcomes,/decision-support - Data governance and validation:
/api/v1/data-residency,/gdpr,/research-export,/medical-validation,/admin/tenants/*/field-validation-rules - EHR import:
/ehr-import,/admin/import - Emergency and incidents:
/emergency,/emergency-medical-info,/incidents - FHIR interoperability:
/fhir/r4 - Hospital configuration and registry:
/hospital-configuration,/hospital-registry - Infection control:
/infection-control - Laboratory:
/laboratory/*,/lab-* - Medical staff:
/medical-staff,/medical-staff/credentials - Medication administration:
/medication-administration,/medication-reconciliation,/adverse-reactions,/barcode-verification - Monitoring and compliance:
/health,/healthcare-monitoring,/clinical-alerts,/compliance,/dashboard,/monitoring,/metrics - Notifications:
/notifications/* - Pathology:
/pathology/* - Patients and portal:
/patients,/guardians,/patient-portal - Pharmacy:
/pharmacy/*,/cds-hooks - Provider-patient coordination:
/provider-patient/handoffs - Queues and jobs:
/jobs,/dlq,/admin/dlq - Reporting:
/reports,/admin/report-schedules - Security and key management:
/healthcare-security,/key-management/kek,/admin/key-management,/csp-report,/admin/api-keys,/admin/secret-rotation - Stellar integration:
/stellar/* - Surgical management:
/surgical - Telemedicine:
/telemedicine/* - Tenant management:
/admin/tenants,/admin/tenant-quota,/onboarding - Webhooks:
/webhooks - Platform services:
/audit,/audit-logs,/api/versions,/consistency,/errors,/i18n,/performance,/admin/query-performance,/admin/reconciliation,/admin/feature-flags,/admin/projections
Tenants can subscribe to healthcare events (record uploads, access grants, diagnosis
updates, and more). Deliveries are signed with HMAC-SHA256 (X-Webhook-Signature),
retried with exponential backoff, replayable from the dead-letter queue, and the
platform also receives signed inbound callbacks from IPFS, Stellar and insurance
payers.
See the webhook integrator guide for the delivery headers, signature verification snippets (Node and Python), secret rotation semantics, retry and alerting behaviour, manual replay, the event catalog, and the inbound signing scheme.
Import docs/postman/MedChain.postman_collection.json. Environments: Local, Testnet, Staging. The collection is generated from OpenAPI; see how to regenerate the API references.
The authoritative database schema is defined by the TypeORM migrations
and corresponding entity definitions in src/.
How the current tenant is resolved, how schema/region routing and Row-Level
Security work, when a new entity needs a tenantId/organizationId column, and
how cron and queue jobs must establish tenant context is documented in
docs/multi-tenancy.md.
Never trust the
x-tenant-idrequest header for authenticated requests — the tenant always comes from the verified JWT. See the doc for details.
All non-FHIR errors return a consistent envelope (statusCode, error, message, code, traceId, timestamp, path, details). Clients should branch on the machine-readable code — one of the AppErrorCode values — rather than on message or the HTTP status. GlobalExceptionFilter is the effective handler: it maps errors to codes, logs them with a traceId, and withholds internal details (stack traces) from responses. Routes under /fhir return an HL7 FHIR OperationOutcome instead.
See docs/errors.md for the full error-code catalog (HTTP status, meaning, and retryability for every code), the exception-filter order, sanitization rules, and how traceId maps to logs and tracing for support requests.
See docs/scripts.md for the full list of test scripts and their prerequisites.
npm run test # Unit tests
npm run test:e2e # End-to-end tests (requires Docker)
npm run test:cov # Unit-test coverage
npm run test:all:cov # Unit and end-to-end coverage
npm run test:compliance # HIPAA/GDPR compliance testsnpm install
npm run start:devThe API is served under the global prefix /api. Swagger docs are available at /api/docs.
Routes are grouped by module. Only routes registered in a module's controllers array are served; any controller not listed there is not exposed.
/auth— authentication and session management/users— user accounts and roles/patients— patient records/appointments— scheduling/pharmacy— prescriptions, dispensing, and inventory/laboratory— lab orders and results/analytics— reporting and dashboards/integrations— external system integrations
- Keep controllers registered in a module and protected by
JwtAuthGuard+RolesGuard. - Derive actor ids from
req.user, never from request bodies. - Update this route list whenever a module's registered controllers change.