Server
The ChartChat server is a Node.js / Express backend that provides the FHIR API, authentication, EHR integration, and all business logic for the platform. It is built on top of the open-source Medplum server with ChartChat-specific extensions.
Directory: packages/server/
Port: 8103 (default)
Entry point: src/index.ts
Key Responsibilities
| Area | Description |
|---|---|
| FHIR R4 | Full FHIR resource CRUD, search, subscriptions, batch operations |
| Authentication | OAuth2 / PKCE — email/password, Google, Microsoft, SMART on FHIR |
| EHR import | OAuth connect + background sync from Epic, Healow, MEDITECH |
| Background jobs | BullMQ workers for bots, async batch, cron, subscriptions |
| AI | Patient summary, chat context, embed sync |
| Security | CSRF, rate limiting, PHI breach detection, audit logging |
| Admin | User management, bot execution, project configuration |
Project Structure
packages/server/src/
├── app.ts ← Express app setup, all route registration
├── index.ts ← Server entry point
├── config/ ← Config loader and type definitions
├── auth/ ← Auth handlers (login, register, MFA, external)
├── oauth/ ← OAuth2 token endpoints and middleware
├── smart/ ← SMART on FHIR launch and patient summary
├── fhir/ ← FHIR R4 routes, resource handlers, operations
├── ehr-import/ ← EHR OAuth flow, sync service, provider configs
├── admin/ ← Admin API routes
├── audit/ ← Audit logging and breach detection
├── security/ ← Security utilities
├── support/ ← Support ticket API
├── workers/ ← BullMQ background job workers
├── migrations/ ← Database schema migrations
├── database.ts ← PostgreSQL connection pool
├── redis.ts ← Redis client
├── ratelimit.ts ← Per-IP rate limiting
├── csrf.ts ← CSRF token validation
└── cors.ts ← CORS allowed origins
Runtime Configuration
The server reads its configuration from two sources at startup:
medplum.config.json
Located in the server's CWD (the release directory in production, packages/server/ locally). Contains non-secret configuration: port, base URL, database connection, Redis connection, feature flags, etc.
{
"port": 8103,
"baseUrl": "http://localhost:8103/",
"appBaseUrl": "http://localhost:3000/",
"binaryStorage": "file:./binary/",
"storageBaseUrl": "http://localhost:8103/storage/",
"awsRegion": "us-east-2",
"supportEmail": "\"ChartChat\" <support@chartchathealth.com>",
"maxJsonSize": "1mb",
"maxBatchSize": "50mb",
"vmContextBotsEnabled": true,
"defaultBotRuntimeVersion": "vmcontext",
"allowedOrigins": "*",
"introspectionEnabled": true,
"saveAuditEvents": true,
"logAuditEvents": true,
"logRequests": true,
"database": {
"host": "localhost",
"port": 5432,
"dbname": "medplum",
"username": "medplum",
"password": "medplum"
},
"redis": {
"host": "localhost",
"port": 6379,
"password": "medplum"
},
"smtp": {
"host": "smtp.gmail.com",
"port": 465,
"username": "help@chartchathealth.com",
"password": "your-smtp-password"
},
"bullmq": {
"removeOnFail": { "count": 100 },
"removeOnComplete": { "count": 100 }
},
"bullBoardPassword": "your-bullboard-password",
"shutdownTimeoutMilliseconds": 30000
}
.env
Contains all secrets and runtime overrides. Create this file at packages/server/.env for local development. Never commit it to version control.
# ── Core ──────────────────────────────────────────────
MEDPLUM_BASE_URL=http://localhost:8103/
MEDPLUM_CLOUD_BASE_URL=https://api.medplum.com/
MEDPLUM_CLIENT_ID=your-medplum-client-id
MEDPLUM_FHIR_URL=http://localhost:8103/fhir/R4/
MEDPLUM_PROJECT_ID=your-medplum-project-id
MEDPLUM_CALLBACK_URL=http://localhost:3000/smart/medplum/callback
MEDPLUM_REGISTER_ENABLED=true
# ── Frontend ──────────────────────────────────────────
FRONTEND_URL=http://localhost:3000
# ── Epic EHR ─────────────────────────────────────────
EPIC_CLIENT_ID=your-epic-client-id
EPIC_REDIRECT_URI=http://localhost:8103/fhir/ehr-import/callback
# ── Healow EHR ────────────────────────────────────────
HEALOW_CLIENT_ID=your-healow-client-id
HEALOW_REDIRECT_URI=http://localhost:8103/fhir/ehr-import/callback
# ── MEDITECH EHR ─────────────────────────────────────
MEDITECH_CLIENT_ID=your-meditech-client-id
MEDITECH_CLIENT_SECRET=your-meditech-client-secret
MEDITECH_REDIRECT_URI=http://localhost:8103/fhir/ehr-import/callback
# ── Security ─────────────────────────────────────────
EHR_TOKEN_ENCRYPTION_KEY=your-32-byte-hex-key
# ── AI ───────────────────────────────────────────────
VITE_GROQ_API_KEY=your-groq-api-key
EMBED_API_URL=http://localhost:8104/
# ── Auth ─────────────────────────────────────────────
GOOGLE_CLIENT_ID=your-google-client-id
RECAPTCHA_SITE_KEY=your-recaptcha-site-key
# ── Defaults ─────────────────────────────────────────
CHARTCHAT_DEFAULT_PASSWORD=your-default-admin-password
Environment Variable Reference
| Variable | Required | Description |
|---|---|---|
MEDPLUM_BASE_URL | Yes | ChartChat server base URL |
MEDPLUM_CLOUD_BASE_URL | No | Medplum cloud API URL |
MEDPLUM_CLIENT_ID | Yes | OAuth ClientApplication ID used by the server |
MEDPLUM_FHIR_URL | Yes | FHIR R4 base URL |
MEDPLUM_PROJECT_ID | Yes | Medplum project ID |
MEDPLUM_CALLBACK_URL | No | OAuth callback URL for SMART auth |
MEDPLUM_REGISTER_ENABLED | No | Allow new account registration. Default: true |
FRONTEND_URL | Yes | Member Portal URL — used for EHR OAuth redirect after callback |
EPIC_CLIENT_ID | No | Epic MyChart OAuth client ID |
EPIC_REDIRECT_URI | No | Epic OAuth redirect URI |
HEALOW_CLIENT_ID | No | Healow (eClinicalWorks) OAuth client ID |
HEALOW_REDIRECT_URI | No | Healow OAuth redirect URI |
MEDITECH_CLIENT_ID | No | MEDITECH OAuth client ID |
MEDITECH_CLIENT_SECRET | No | MEDITECH OAuth client secret |
MEDITECH_REDIRECT_URI | No | MEDITECH OAuth redirect URI |
EHR_TOKEN_ENCRYPTION_KEY | Yes | AES encryption key for stored EHR tokens. Must be a 32-byte hex string. |
VITE_GROQ_API_KEY | No | Groq API key for AI chat and patient summary |
EMBED_API_URL | No | Base URL of the AI embedding service. Called as POST {EMBED_API_URL}/api/embed/{patientId}/sync after every EHR sync to re-index patient records for the AI chat. Defaults to https://dev-chatapi.chartchathealth.com. |
GOOGLE_CLIENT_ID | No | Google OAuth client ID |
RECAPTCHA_SITE_KEY | No | Google reCAPTCHA site key |
CHARTCHAT_DEFAULT_PASSWORD | No | Default password for seeded admin accounts |
EHR_TOKEN_ENCRYPTION_KEY must be set to a cryptographically random 32-byte key in production. Generate one with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
How to Run Locally
See Local Setup for the full guide. Summary:
# 1. Start PostgreSQL + Redis
docker-compose up # from repo root
# 2. Build shared packages
npm run build:fast
# 3. Start the server
cd packages/server
npm install
npm run dev
The server starts at http://localhost:8103.