Lab testing application: lab orders, result ingestion (FHIR), patient biomarker history, and reference data management. Backend API (Express + Firestore) and React frontend (Vite + Tailwind).
- Prerequisites: Node.js (v18+), npm or yarn.
- Install dependencies:
cd backend npm install - Environment: Copy
.env.exampleto.envand configure:PORT— Server port (default4000).USE_FIRESTORE=true— Required to use Firestore.- Firebase credentials (one of):
GOOGLE_APPLICATION_CREDENTIALS— Path to Firebase service account JSON (e.g../firebase-service-account.json).FIREBASE_SERVICE_ACCOUNT_JSON— Inline JSON string of the service account (do not commit). Do not commit credentials; add the JSON file to.gitignore.
- Build and run:
Or for development with watch:
npm run build npm start
API runs atnpm run dev
http://localhost:4000. Health:GET /health. API docs:http://localhost:4000/docs. - Seed data (optional):
npm run seed— seeds Firestore with sample patients, labs, biomarkers, lab tests, orders, and results (usesseedFirestore(true)). With default store behavior, empty Firestore is also auto-seeded on first use.
- Prerequisites: Node.js (v18+), npm or yarn.
- Install dependencies:
cd frontend npm install - Environment: Copy
.env.exampleto.env.local(or.env) and set:VITE_API_URL=http://localhost:4000— Backend API base URL (no trailing slash).
- Run:
App runs at
npm run dev
http://localhost:3000(Vite default). For production build:npm run build; preview:npm run preview.
- Runtime: Node.js, Express, TypeScript (compiled to
dist/). - Entry:
src/index.ts— loadsdotenv, mountsapp, listens onPORT(default 4000). - App:
src/app.ts— CORS, JSON body parser, servesopenapi.json, Scalar API docs at/docs, health at/health, and mounts:/orders— lab orders (create, list, get, patch, delete, send to lab)./results— lab results (list, delete) and FHIR ingest atPOST /results/ingest./patients— patient get and biomarker history./(reference) — reference data:/labs,/biomarkers,/lab-tests(CRUD) and treatment options.
- Store: Single persistence abstraction
IStoreinsrc/store/types.ts, implemented by Firestore insrc/store/firestore.ts.getStore()insrc/store/index.tsreturns the singleton; it requiresUSE_FIRESTORE=trueand valid Firebase credentials. Firestore is initialized fromGOOGLE_APPLICATION_CREDENTIALS(file path) orFIREBASE_SERVICE_ACCOUNT_JSON(inline JSON). Empty Firestore is auto-seeded once with sample data. - Routes → services → store: Routes in
src/routes/call services insrc/services/(e.g.orders,results,patients,referenceData), which usegetStore()and domain types fromsrc/types/lab.ts. - FHIR/HL7: Result ingestion uses
src/mapping/fhirToResult.ts(FHIR Observation/Bundle → internalLabResult). Outbound lab integration uses adapters insrc/adapters/:hl7AdapterandfhirAdapter(selected by lab’sintegrationType). Order → HL7/FHIR mapping lives insrc/mapping/orderToHl7.tsandorderToFhir.ts. - API spec:
openapi.jsonat repo root describes the ReadyRX Lab Testing API; Scalar consumes it for/docs.
- Stack: React 18, TypeScript, Vite, React Router v6, Tailwind CSS.
- Entry:
index.html→/src/main.tsx—BrowserRouter(with v7 future flags),App, globalindex.css. - Routing:
App.tsx—/(dashboard),/admin/*(nested: labs, biomarkers, lab-tests, orders, results); admin index redirects to/admin/labs; catch-all redirects to/. - Pages: Dashboard at
src/pages/DashboardPage.tsx; admin undersrc/pages/admin/(AdminLayout, AdminLabsPage, AdminBiomarkersPage, AdminLabTestsPage, AdminOrdersPage, AdminResultsPage). - API client:
src/lib/api.ts— base URL fromimport.meta.env.VITE_API_URL(defaulthttp://localhost:4000). Functions for patients, orders, results, labs, biomarkers, lab tests (CRUD and list); all list responses normalized to arrays. - UI: Reusable components in
src/components/ui/(button, card, input, label, select, table, badge, textarea) using Tailwind,class-variance-authority(cva), andcn()fromsrc/lib/utils.ts(clsx + tailwind-merge). Dashboard-specific components insrc/components/dashboard/(e.g. LabOrdersSection, RecentLabResults, BiomarkerTrendChart, AITreatmentSuggestion). - Data: Shared types in
src/types/lab.ts;src/data/mockData.tsandsrc/data/trendHelpers.tsfor dashboard/trend helpers.
- Firestore-only persistence: The app uses a single
IStoreimplementation (Firestore). No in-memory or other backends;getStore()throws if Firestore is not enabled or credentials are missing. This keeps deployment simple and aligns with a single source of truth. - OpenAPI + Scalar: API contract is defined in
openapi.jsonand served at/openapi.json; Scalar provides interactive docs at/docswithout requiring code generation from the spec. - FHIR ingest, internal model: Results can be ingested via FHIR Observation/Bundle at
POST /results/ingest; mapping normalizes to internalLabResultand stores in Firestore. This allows interoperability while keeping the rest of the API and UI on a stable internal schema. - Lab-specific adapters (HL7 vs FHIR): Labs have an
integrationType(HL7orFHIR). Adapters insrc/adapters/produce outbound payloads; mapping from internal orders to HL7/FHIR lives insrc/mapping/. This keeps integration logic pluggable per lab. - Simulated inbound results: Creating an order triggers a delayed, simulated inbound result (after a few seconds) for demo/development, writing one result and updating the order status. Production would replace this with real lab integrations.
- Seed script and auto-seed:
npm run seedrunsseedFirestore(true). The Firestore store also runs a one-time auto-seed when the DB is empty so the app is usable without running the seed script explicitly. - Port and env: Server port is configurable via
PORT; all secrets and config come from environment variables (and.envvia dotenv), with.envgitignored.
- Vite + React: Vite for fast dev and ESM-based builds; React 18 with
createRoot. Path alias@→src/for cleaner imports. - Single API base URL: Backend URL is configured once via
VITE_API_URL; the API module uses it for all requests and normalizes list responses to arrays for predictable consumption. - Route-based structure: Dashboard and admin are separate route trees; admin uses a shared layout (
AdminLayout) and nested routes for labs, biomarkers, lab tests, orders, and results. This keeps feature boundaries clear. - Tailwind + CVA +
cn(): Styling is utility-first with Tailwind. Components useclass-variance-authorityfor variant props (e.g. button variant/size) and a smallcn()helper (clsx + tailwind-merge) to merge and override classes without conflicts. - No global state library: Data is fetched via the API module and local state; no Redux or similar. Suitable for current scope and keeps the frontend simple.
- Shared types:
src/types/lab.tsmirrors domain concepts (Patient, Lab, Biomarker, LabTest, LabOrder, LabResult, etc.) used by the API client and components, keeping the frontend aligned with the backend contract.