-
Notifications
You must be signed in to change notification settings - Fork 7
Add canonical mapping example (HIS to FHIR) #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
273 changes: 273 additions & 0 deletions
273
aidbox-features/aidbox-canonical-mapping/.claude/skills/generate-types.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,273 @@ | ||
| --- | ||
| name: generate-types | ||
| description: Generate or update FHIR TypeScript types using @atomic-ehr/codegen with tree-shaking | ||
| --- | ||
|
|
||
| # FHIR Type Generation with @atomic-ehr/codegen | ||
|
|
||
| Generate type-safe FHIR R4 TypeScript interfaces from StructureDefinitions. Uses tree-shaking to only include the resource types you need. | ||
|
|
||
| ## Setup (if not already configured) | ||
|
|
||
| ### 1. Install the codegen package | ||
|
|
||
| ```sh | ||
| # npm | ||
| npm install -D @atomic-ehr/codegen | ||
|
|
||
| # pnpm | ||
| pnpm add -D @atomic-ehr/codegen | ||
|
|
||
| # yarn | ||
| yarn add -D @atomic-ehr/codegen | ||
|
|
||
| # bun | ||
| bun add -d @atomic-ehr/codegen | ||
| ``` | ||
|
|
||
| For npm/pnpm/yarn projects, also install `tsx` to run the TypeScript generation script: | ||
|
|
||
| ```sh | ||
| npm install -D tsx | ||
| ``` | ||
|
|
||
| ### 2. Create the generation script | ||
|
|
||
| Create `scripts/generate-types.ts`: | ||
|
|
||
| ```ts | ||
| import { APIBuilder, prettyReport } from "@atomic-ehr/codegen"; | ||
|
|
||
| const builder = new APIBuilder() | ||
| .throwException() | ||
| .fromPackage("hl7.fhir.r4.core", "4.0.1") | ||
| .typescript({ | ||
| withDebugComment: false, | ||
| generateProfile: false, | ||
| openResourceTypeSet: false, | ||
| }) | ||
| .typeSchema({ | ||
| treeShake: { | ||
| "hl7.fhir.r4.core": { | ||
| // Add the resource types you need here. | ||
| // All dependency types (Identifier, Reference, CodeableConcept, etc.) | ||
| // are included automatically. | ||
| "http://hl7.org/fhir/StructureDefinition/Patient": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Bundle": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/OperationOutcome": {}, | ||
| }, | ||
| }, | ||
| }) | ||
| .outputTo("./src/fhir-types") | ||
| .cleanOutput(true); | ||
|
|
||
| const report = await builder.generate(); | ||
| console.log(prettyReport(report)); | ||
| if (!report.success) process.exit(1); | ||
| ``` | ||
|
|
||
| ### 3. Add the script to package.json | ||
|
|
||
| ```json | ||
| { | ||
| "scripts": { | ||
| "generate-types": "bun run scripts/generate-types.ts" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| For npm/pnpm/yarn projects (using tsx instead of bun): | ||
|
|
||
| ```json | ||
| { | ||
| "scripts": { | ||
| "generate-types": "tsx scripts/generate-types.ts" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### 4. Add to .gitignore | ||
|
|
||
| ``` | ||
| .codegen-cache/ | ||
| ``` | ||
|
|
||
| ### 5. Include scripts in tsconfig.json | ||
|
|
||
| ```json | ||
| { | ||
| "include": ["src/**/*", "scripts/**/*"] | ||
| } | ||
| ``` | ||
|
|
||
| ### 6. Run generation | ||
|
|
||
| ```sh | ||
| # bun | ||
| bun run generate-types | ||
|
|
||
| # npm | ||
| npm run generate-types | ||
|
|
||
| # pnpm | ||
| pnpm run generate-types | ||
| ``` | ||
|
|
||
| This outputs typed interfaces to `src/fhir-types/hl7-fhir-r4-core/`. Commit these files — they are the project's FHIR type definitions. | ||
|
|
||
| ## APIBuilder reference | ||
|
|
||
| ### Loading FHIR packages | ||
|
|
||
| ```ts | ||
| // FHIR R4 base (required) | ||
| .fromPackage("hl7.fhir.r4.core", "4.0.1") | ||
|
|
||
| // Implementation Guides (optional) — load from tgz URL | ||
| .fromPackageRef("https://fs.get-ig.org/-/hl7.fhir.us.core-7.0.0.tgz") | ||
| .fromPackageRef("https://fs.get-ig.org/-/fhir.r4.ukcore.stu2-2.0.2.tgz") | ||
| ``` | ||
|
|
||
| ### TypeScript options | ||
|
|
||
| ```ts | ||
| .typescript({ | ||
| withDebugComment: false, // omit debug comments in generated files | ||
| generateProfile: false, // set true when loading IGs with constrained profiles | ||
| openResourceTypeSet: false, // stricter resource type unions | ||
| }) | ||
| ``` | ||
|
|
||
| ### Tree-shaking | ||
|
|
||
| Tree-shaking controls which resource types are generated. Without it, ALL FHIR resources are included (~150+ files). With it, only the listed types and their transitive dependencies are generated. | ||
|
|
||
| ```ts | ||
| .typeSchema({ | ||
| treeShake: { | ||
| "<package-name>": { | ||
| "<StructureDefinition canonical URL>": {}, | ||
| // ... | ||
| }, | ||
| }, | ||
| }) | ||
| ``` | ||
|
|
||
| **StructureDefinition URL patterns:** | ||
|
|
||
| | Source | Pattern | Example | | ||
| |--------|---------|---------| | ||
| | FHIR R4 base | `http://hl7.org/fhir/StructureDefinition/{ResourceType}` | `http://hl7.org/fhir/StructureDefinition/Patient` | | ||
| | US Core | `http://hl7.org/fhir/us/core/StructureDefinition/{profile}` | `http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient` | | ||
| | UK Core | `https://fhir.hl7.org.uk/StructureDefinition/{profile}` | `https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient` | | ||
|
|
||
| **Common FHIR R4 resource types:** | ||
|
|
||
| ```ts | ||
| "http://hl7.org/fhir/StructureDefinition/Patient": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Encounter": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Observation": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Condition": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Procedure": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/MedicationRequest": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/DiagnosticReport": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/AllergyIntolerance": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Immunization": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Location": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Organization": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Practitioner": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Bundle": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/OperationOutcome": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/Questionnaire": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse": {}, | ||
| ``` | ||
|
|
||
| ### Output options | ||
|
|
||
| ```ts | ||
| .outputTo("./src/fhir-types") // output directory | ||
| .cleanOutput(true) // delete output dir before regenerating | ||
| ``` | ||
|
|
||
| ## Example: Adding an Implementation Guide | ||
|
|
||
| To generate US Core profiled types alongside base R4: | ||
|
|
||
| ```ts | ||
| import { APIBuilder, prettyReport } from "@atomic-ehr/codegen"; | ||
|
|
||
| const builder = new APIBuilder() | ||
| .throwException() | ||
| .fromPackage("hl7.fhir.r4.core", "4.0.1") | ||
| .fromPackageRef("https://fs.get-ig.org/-/hl7.fhir.us.core-7.0.0.tgz") | ||
| .typescript({ | ||
| withDebugComment: false, | ||
| generateProfile: true, // enable for IG profiles | ||
| openResourceTypeSet: false, | ||
| }) | ||
| .typeSchema({ | ||
| treeShake: { | ||
| "hl7.fhir.r4.core": { | ||
| "http://hl7.org/fhir/StructureDefinition/Bundle": {}, | ||
| "http://hl7.org/fhir/StructureDefinition/OperationOutcome": {}, | ||
| }, | ||
| "hl7.fhir.us.core": { | ||
| "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient": {}, | ||
| "http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition-encounter-diagnosis": {}, | ||
| "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab": {}, | ||
| }, | ||
| }, | ||
| }) | ||
| .outputTo("./src/fhir-types") | ||
| .cleanOutput(true); | ||
|
|
||
| const report = await builder.generate(); | ||
| console.log(prettyReport(report)); | ||
| if (!report.success) process.exit(1); | ||
| ``` | ||
|
|
||
| ## What gets generated | ||
|
|
||
| For each resource type, a `.ts` file is generated containing: | ||
|
|
||
| - **Main interface** — e.g., `Patient`, `Encounter`, `Bundle` | ||
| - **BackboneElement interfaces** — nested structures like `EncounterLocation`, `PatientContact`, `BundleEntry` | ||
| - **Type guard function** — e.g., `isPatient(resource)`, `isEncounter(resource)` | ||
| - **Re-exports** of dependency types (Identifier, Reference, CodeableConcept, etc.) | ||
| - **Barrel export** — `index.ts` re-exports everything for convenience | ||
|
|
||
| Generated types are fully typed with FHIR value set enums where applicable: | ||
|
|
||
| ```ts | ||
| // Encounter.status is a union of valid FHIR values | ||
| status: ("planned" | "arrived" | "triaged" | "in-progress" | "onleave" | "finished" | "cancelled" | "entered-in-error" | "unknown"); | ||
|
|
||
| // References are typed with target resource types | ||
| subject?: Reference<"Group" | "Patient">; | ||
| location: Reference<"Location">; | ||
| ``` | ||
|
|
||
| ## Import patterns | ||
|
|
||
| ```ts | ||
| // Direct file import (preferred — explicit about what you use) | ||
| import type { Patient } from "./fhir-types/hl7-fhir-r4-core/Patient"; | ||
| import type { Encounter, EncounterLocation } from "./fhir-types/hl7-fhir-r4-core/Encounter"; | ||
| import type { Bundle, BundleEntry } from "./fhir-types/hl7-fhir-r4-core/Bundle"; | ||
| import type { Identifier } from "./fhir-types/hl7-fhir-r4-core/Identifier"; | ||
| import type { Reference } from "./fhir-types/hl7-fhir-r4-core/Reference"; | ||
|
|
||
| // Barrel import (convenient for grabbing many types) | ||
| import type { Patient, Encounter, Location, Bundle } from "./fhir-types/hl7-fhir-r4-core"; | ||
|
|
||
| // Type guards (value imports, not type-only) | ||
| import { isPatient, isEncounter } from "./fhir-types/hl7-fhir-r4-core"; | ||
| ``` | ||
|
|
||
| ## Workflow for adding a new resource type | ||
|
|
||
| 1. Edit `scripts/generate-types.ts` — add the StructureDefinition URL to `treeShake` | ||
| 2. Run `npm run generate-types` (or `bun run generate-types`, `pnpm run generate-types`) | ||
| 3. Import the new type in your code | ||
| 4. Run typecheck to verify | ||
| 5. Commit the updated `src/fhir-types/` directory | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # FHIR Facade Configuration | ||
|
|
||
| # Server | ||
| PORT=3000 | ||
| CACHE_TTL_SECONDS=60 | ||
| REDIS_URL=redis://redis:6379 | ||
|
|
||
| # HIS (Hospital Information System) API | ||
| HIS_BASE_URL=http://his:4000 | ||
| HIS_CLIENT_ID=his-client | ||
| HIS_CLIENT_SECRET=his-secret | ||
| HIS_ENVIRONMENT=TEST | ||
|
|
||
| # Requesting Product header value | ||
| REQUESTING_PRODUCT=FHIR-Facade/1.0.0 | ||
|
|
||
| # Event-Driven Architecture | ||
| RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672 | ||
| FHIR_SERVER_URL=http://aidbox:8080 | ||
| AIDBOX_CLIENT_ID=root | ||
| AIDBOX_CLIENT_SECRET=WdodyB65ij | ||
| PREFETCH=10 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # Dependencies | ||
| node_modules/ | ||
|
|
||
| # Environment | ||
| .env | ||
| .env.local | ||
| .env.*.local | ||
|
|
||
| # Build | ||
| dist/ | ||
| *.tsbuildinfo | ||
|
|
||
| # IDE | ||
| .idea/ | ||
| .vscode/ | ||
| *.swp | ||
| *.swo | ||
| *~ | ||
|
|
||
| # OS | ||
| .DS_Store | ||
| Thumbs.db | ||
|
|
||
| # Logs | ||
| *.log | ||
| npm-debug.log* | ||
|
|
||
| # Bun | ||
| bun.lock | ||
|
|
||
| # Codegen | ||
| .codegen-cache/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # CLAUDE.md | ||
|
|
||
| ## Project Overview | ||
|
|
||
| This project demonstrates canonical mapping — translating proprietary Hospital Information System (HIS) data into FHIR R4 resources. It shows two architectural approaches: | ||
|
|
||
| 1. **Pure Facade** (synchronous) — Redis-cached proxy that fetches from HIS API on demand | ||
| 2. **Event-Driven** — RabbitMQ consumer that maps ADT events to FHIR and stores in Aidbox | ||
|
|
||
| ## FHIR Type Generation | ||
|
|
||
| FHIR TypeScript types are generated using `@atomic-ehr/codegen`. The config is at `scripts/generate-types.ts` with tree-shaking for Patient, Encounter, Location, Bundle, and OperationOutcome. | ||
|
|
||
| Generated types are output to `src/fhir-types/hl7-fhir-r4-core/` — do not edit these files manually. | ||
|
|
||
| ```ts | ||
| import type { Patient } from "./fhir-types/hl7-fhir-r4-core/Patient"; | ||
| import type { Encounter, EncounterLocation } from "./fhir-types/hl7-fhir-r4-core/Encounter"; | ||
| import type { Bundle, BundleEntry } from "./fhir-types/hl7-fhir-r4-core/Bundle"; | ||
| ``` | ||
|
|
||
| To add a new resource type, add its StructureDefinition URL to the `treeShake` config and run `bun run generate-types`. | ||
|
|
||
| ## Commands | ||
|
|
||
| | Command | Description | | ||
| |---------|-------------| | ||
| | `bun run generate-types` | Regenerate FHIR TypeScript types | | ||
| | `bun run typecheck` | TypeScript type check | | ||
| | `bun run dev` | Start facade with hot reload | | ||
| | `bun run start:consumer` | Start event consumer | | ||
| | `bun run publish:admit` | Publish 7 test admit events | | ||
| | `docker compose --profile facade up -d --build` | Run facade approach | | ||
| | `docker compose --profile event-driven up -d --build` | Run event-driven approach | | ||
|
|
||
| ## Project Structure | ||
|
|
||
| - `src/facade/index.ts` — FHIR facade HTTP server (Bun.serve) | ||
| - `src/facade/cache.ts` — Redis TTL cache | ||
| - `src/facade/his-server.ts` — Sample HIS API server | ||
| - `src/event-driven/consumer.ts` — RabbitMQ ADT event consumer | ||
| - `src/event-driven/publisher.ts` — ADT event simulator | ||
| - `src/event-driven/fhir-client.ts` — Aidbox FHIR client | ||
| - `src/shared/his-client.ts` — HIS API client (OAuth 2.0) | ||
| - `src/shared/fhir-mapper.ts` — HIS → FHIR R4 mapping functions | ||
| - `src/shared/test-data.ts` — Shared test data (7 sample patients) | ||
| - `src/fhir-types/` — Auto-generated FHIR types (do not edit) | ||
| - `scripts/generate-types.ts` — Codegen configuration |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it same as https://github.com/HealthSamurai/samurai-skills/tree/master ?