0x0a · project

E-Dokita EMR

Backend Engineer

E-Dokita EMR

A healthcare API gateway and clinical workflow layer over OpenEMR — a small, typed, role-aware Express 5 surface on two persistence systems: PostgreSQL for identity, consent and coordination, and OpenEMR as the clinical source of truth.

stack

TypeScript Express 5 PostgreSQL Drizzle ORM OpenEMR FHIR R4 Argon2id RS256 JWT AsyncLocalStorage Zod Docker

01 · Requirement Analysis

A clinical product needed a modern, typed, role-aware HTTP surface over OpenEMR — without inventing a second clinical record. The brief split cleanly: OpenEMR stays the system of record for patients, practitioners, encounters, observations, prescriptions and labs, while the API owns application identity, sessions, EMR consent, appointment overlays, working hours, waitlists, messaging and notifications.

02 · Architecture Discovery

Charted both systems before writing a line. Mapped what the API owns against what OpenEMR owns, then chose a modular monolith over separate services because the domain shares one identity and one upstream. Contexts — the OpenEMR client and the current PostgreSQL transaction — propagate through AsyncLocalStorage instead of being threaded through every function signature, and cross-system workflows are designed as sequential, idempotent steps because no distributed transaction spans the two stores.

03 · Software Architecture

Express 5 composed in strict layers — route, controller, service, helper — across sixteen domain modules (auth, user, admin, doctor, patient, appointment, schedule, emr, emr-access, lab, prescription, waitlist, messaging, notification, openemr, system). PostgreSQL through Drizzle holds identity, sessions, consent grants, overlays and messaging; an OpenEMRClient wraps FHIR R4 and the Standard REST API with typed helpers, per-user OAuth2 Authorization Code + PKCE for staff and a cached service account for patient flows; a custom PHP extension in a digest-pinned OpenEMR image adds lab and observation endpoints; and the OpenAPI document is generated from the same Zod schemas that validate requests.

04 · Development Principles

A thin HTTP boundary ordered authenticate → withOpenEMR → requireRole → validate → resource guards, with services returning [value, error] tuples that controllers unwrap into one response envelope. Authorization is layered: role, then ownership or a patient-approved, time-limited consent grant, then upstream OpenEMR scopes as a second boundary. No network call is ever wrapped in a PostgreSQL transaction, and @Transactional stays on DB-only private methods. Credentials are Argon2id hashes, 15-minute RS256 access tokens, hashed rotating refresh tokens and a session epoch that invalidates every earlier token.

05 · Systems Design

Decoupled the domain so availability holds under high-load spikes — identity, clinical reads, scheduling, labs and messaging degrade and recover independently instead of dragging the whole surface down. Authored strict API contracts and normalized schemas for the dual-sided patient and provider workflows, and kept every external clinical provider behind an explicit integration layer that handles partial failure states and transactional boundaries rather than assuming they succeed.

06 · Impact

One gateway now serves patients, doctors, admins and lab staff over a single contract: consent-gated chart access that expires on its own, real reconciliation paths for writes that span two stores, and failure contained to one module instead of corrupting state.

CLIENT GATEWAY OPENEMR