HIPAA-compliant medical case management API built on Cloudflare Workers.
UltraCare Backend is a serverless REST API that powers a medical diagnostic center management platform. It handles patient records, clinical case management, report generation, referral tracking, billing/collections, and staff operations — all with HIPAA-compliant security, audit logging, and multi-tenant isolation.
- Case Management — Full lifecycle from patient intake to report delivery
- Clinical Reports — Rich-text reports with image attachments, templates, and export (PDF/Excel/HTML)
- Referral System — Referral doctor management with commission tracking and analytics
- Collections & Billing — Revenue reports with financial year scoping and multi-format export
- Staff Management — Staff CRUD, attendance tracking, salary records
- Organization Settings — Multi-tenant configuration, service pricing, report templates
- Form F Generation — Regulatory document generation from case data
- File Storage — S3-compatible uploads via Backblaze B2
- Dashboards — Role-specific analytics for doctors and staff
| Technology | Purpose |
|---|---|
| Cloudflare Workers | Edge compute runtime |
| Hono | HTTP framework |
| Neon Serverless Postgres | Database |
| Drizzle ORM | Type-safe query builder |
| Zod | Request validation |
| Backblaze B2 | File/image storage (S3 API) |
| Sentry (Cloudflare SDK) | Error monitoring with PII scrubbing |
| Resend | Transactional email (via Fetch API) |
| TypeScript | Language |
| ExcelJS | Excel report generation |
- Node.js ≥ 20
- npm ≥ 9
- A Neon Postgres database
- A Cloudflare account (for deployment)
# Clone the repository
git clone https://github.com/your-org/ultracare-backend.git
cd ultracare-backend
# Install dependencies
npm install# Copy the example environment file
cp .env.example .dev.varsEdit .dev.vars with your credentials:
JWT_SECRET= # openssl rand -base64 64
DATABASE_URL= # postgresql://<user>:<pass>@<host>.neon.tech/<db>?sslmode=require
SENTRY_DSN= # From Sentry project settings
B2_KEY_ID= # Backblaze B2 Application Key ID
B2_APP_KEY= # Backblaze B2 Application Key
B2_BUCKET_NAME= # e.g. ultracare-prod
B2_ENDPOINT= # e.g. s3.us-west-004.backblazeb2.com
RESEND_API_KEY= # From Resend dashboard
EMAIL_FROM= # Optional: 'Name <email@domain.com>'Note: Wrangler reads
.dev.varsautomatically for local development.
npm run devThe API server starts at http://localhost:8787.
Verify it's running:
curl http://localhost:8787/api/health
# → { "status": "ok" }| Command | Description |
|---|---|
npm run dev |
Start local dev server (port 8787) |
npm run build |
Compile TypeScript |
npm run deploy |
Deploy to Cloudflare Workers |
npm test |
Run all tests |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Run tests with coverage report |
npx drizzle-kit generate # Generate migration from schema changes
npx drizzle-kit push # Apply migrations to database
npx drizzle-kit studio # Open Drizzle Studio GUIAll endpoints are prefixed with /api. Authentication is via Authorization: Bearer <token> header unless noted as public.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/health |
No | Health check |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/doctor-login |
No | Doctor login |
| POST | /api/auth/doctor-register |
No | Doctor registration |
| POST | /api/auth/staff-login |
No | Staff login |
| POST | /api/auth/staff-forgot-password |
No | Staff password reset |
| POST | /api/auth/admin-reset-password |
Yes | Admin resets staff password |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/cases |
Yes | List cases (paginated) |
| POST | /api/cases |
Yes | Create a new case |
| GET | /api/cases/today |
Yes | Today's cases |
| GET | /api/cases/:caseId |
Yes | Get case details |
| PUT | /api/cases/:caseId |
Yes | Update a case |
| DELETE | /api/cases/:caseId |
Yes | Soft-delete a case |
| GET | /api/cases/:caseId/visits |
Yes | List visits for a case |
| GET | /api/cases/:caseId/reports |
Yes | List reports for a case |
| GET | /api/cases/:caseId/reports/:reportId |
Yes | Get a specific report |
| POST | /api/cases/:caseId/reports/:reportId/email |
Yes | Email a report |
| GET | /api/cases/:caseId/reports/:reportId/print |
Yes | Print-ready report view |
| GET | /api/cases/:caseId/reports/:reportId/export/html |
Yes | Export report as HTML |
| PATCH | /api/cases/:caseId/reports/:reportId/image |
Yes | Update report image |
| PATCH | /api/cases/:caseId/reports/:reportId/description |
Yes | Update report description |
| GET | /api/cases/:caseId/form-f |
Yes | Generate Form F |
| GET | /api/cases/:caseId/documents |
Yes | List case documents |
| DELETE | /api/cases/:caseId/documents/:documentId |
Yes | Delete a document |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/reports/:caseId |
Yes | List reports |
| POST | /api/reports/:caseId |
Yes | Create a report |
| GET | /api/reports/:caseId/:reportId |
Yes | Get report |
| PUT | /api/reports/:caseId/:reportId |
Yes | Update report |
| PATCH | /api/reports/:caseId/:reportId/status |
Yes | Update report status |
| POST | /api/reports/:caseId/:reportId/images |
Yes | Upload report images |
| PUT | /api/reports/:caseId/:reportId/images |
Yes | Update image metadata |
| DELETE | /api/reports/:caseId/:reportId/images |
Yes | Delete report images |
| POST | /api/reports/:caseId/:reportId/images/reorder |
Yes | Reorder report images |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/patients/search |
Yes | Search patients |
| GET | /api/patients/:patientId/chronology |
Yes | Patient case history |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/organization/setup |
Yes | Initial org setup |
| GET | /api/organization/info |
Yes | Get org info |
| PUT | /api/organization/info |
Yes | Update org info |
| GET | /api/organization/dashboard |
Yes | Org dashboard metrics |
| GET | /api/organization/settings/pricing |
Yes | Get service pricing |
| PUT | /api/organization/settings/pricing |
Yes | Update service pricing |
| GET | /api/organization/templates |
Yes | List report templates |
| PUT | /api/organization/templates/:reportType |
Yes | Update a report template |
| GET | /api/organization/staff |
Yes | List staff members |
| POST | /api/organization/staff |
Yes | Add a staff member |
| DELETE | /api/organization/staff/:staffId |
Yes | Remove a staff member |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/referral/doctors |
Yes | List referral doctors |
| POST | /api/referral/doctors |
Yes | Add referral doctor |
| DELETE | /api/referral/doctors/:doctorId |
Yes | Remove referral doctor |
| GET | /api/referral/doctors/:doctorId/report |
Yes | Referral doctor report |
| GET | /api/referral/doctors/:doctorId/export/pdf |
Yes | Export referral report as PDF |
| GET | /api/referral/dashboard/export |
Yes | Export referral dashboard |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/collection |
Yes | Collection report data |
| GET | /api/collection/export/pdf |
Yes | Export as PDF |
| GET | /api/collection/export/xlsx |
Yes | Export as Excel |
| POST | /api/collection/export/modified |
Yes | Modified collection report |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/upload/file |
Yes | List uploaded files |
| POST | /api/upload/file |
Yes | Upload a file |
| GET | /api/upload/signed-url |
Yes | Get signed download URL |
| GET | /api/upload/:filename |
Yes | Get file by name |
| DELETE | /api/upload/:filename |
Yes | Delete a file |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/dashboard/staff |
Yes | Staff dashboard |
| GET | /api/dashboard/doctor |
Yes | Doctor dashboard |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/contact |
No | Submit contact form |
| GET | /api/user |
Yes | Get user profile |
| PUT | /api/user |
Yes | Update user profile |
| PUT | /api/user/picture |
Yes | Update profile picture |
| GET | /api/debug-sentry |
No | Force error for Sentry |
| GET | /api/logs |
Yes | Activity logs |
├── src/
│ ├── worker.ts # Entry point (Sentry wrapper)
│ ├── app.ts # Hono app + middleware chain
│ ├── env.ts # Environment type definitions
│ ├── routes/ # Route definitions
│ ├── controllers/ # Request handling + validation
│ ├── services/ # Business logic
│ ├── db/ # Schema, client, repository
│ ├── middleware/ # Auth, audit, rate limiting
│ ├── schemas/ # Zod validation schemas
│ ├── utils/ # Crypto, storage, export, email
│ ├── views/ # Handlebars report templates
│ ├── types/ # TypeScript type definitions
│ └── __tests__/ # Jest test suites
├── drizzle/ # Database migrations
├── .github/workflows/ # CI/CD (deploy-wrangler.yml)
├── wrangler.toml # Cloudflare Workers config
├── ARCHITECTURE.md # System architecture documentation
└── CLAUDE.md # AI coding assistant context
See ARCHITECTURE.md for detailed system design, request lifecycle, and database schema diagrams.
Deployments are triggered automatically via GitHub Actions:
- Push to
stage→ deploys to the staging environment - Push to
main→ deploys to production
npm run deployProduction secrets are set via Wrangler CLI:
npx wrangler secret put DATABASE_URL
npx wrangler secret put JWT_SECRET
npx wrangler secret put SENTRY_DSN
npx wrangler secret put B2_KEY_ID
npx wrangler secret put B2_APP_KEY
npx wrangler secret put RESEND_API_KEY# Run all tests
npm test
# Run a specific test file
npm test -- src/__tests__/controllers/authController.test.ts
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverageTests use Jest with ts-jest. Coverage is tracked for controllers/, utils/, middleware/, and schemas/.
This API handles electronic Protected Health Information (ePHI) and implements the following HIPAA Security Rule controls:
| Control | Implementation |
|---|---|
| Audit Controls (§164.312(b)) | Structured audit logs on every /api/* request |
| Access Controls (§164.312(a)) | JWT authentication + role-based guards + org-level isolation |
| Transmission Security (§164.312(e)) | HTTPS enforced via HSTS with preload |
| PII Protection | Sentry beforeSend strips request bodies, cookies, and auth headers |
All responses include: HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer, and a restrictive Permissions-Policy.
Authentication endpoints are rate-limited via Cloudflare KV:
- Registration: 5 requests / 10 min per IP
- Login: 10 requests / 10 min per IP
ISC — see package.json for details.