@onivoro/isomorphic-fhir 24.38.2 → 25.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,44 +1,53 @@
1
1
  # @onivoro/isomorphic-fhir
2
2
 
3
- A library for transforming FHIR (Fast Healthcare Interoperability Resources) bundles into normalized database tables following Third Normal Form (3NF).
3
+ A library for flattening FHIR (Fast Healthcare Interoperability Resources) R4 bundles into a set of relational-style tables (plain arrays of row objects), plus TypeScript interfaces for the supported FHIR resources and the generated tables.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @onivoro/isomorphic-fhir
9
+ ```
4
10
 
5
11
  ## Overview
6
12
 
7
- This library takes FHIR R4 bundles containing healthcare resources and transforms them into a set of normalized tables that can be used to populate a relational database. The transformation process:
13
+ `normalizeFhirBundle` walks a bundle's entries and splits each supported resource into a main row plus child rows for its repeating and nested parts. Along the way it:
8
14
 
9
- - Eliminates data redundancy across FHIR resources
10
- - Ensures referential integrity between healthcare entities
11
- - Creates proper foreign key relationships
12
- - Generates unique IDs for normalized records
13
- - Preserves FHIR resource relationships through reference tables
14
- - Handles complex nested structures like CodeableConcepts and Extensions
15
+ - Generates a new UUID (v4) `id` for every row and links child rows to their parent through that id (e.g. `patientNames[].patientId`, `codings[].codeableConceptId`)
16
+ - Keeps the original FHIR `id` in `resourceId` on main rows
17
+ - Stores shared data types (addresses, telecoms, CodeableConcepts and codings, notes, attachments, references) in generic tables keyed by `resourceType` + `resourceId` (+ `fieldName` where relevant)
18
+ - Copies FHIR reference strings (e.g. `"Patient/patient-123"`) as-is into `*Reference` columns and the `references` table; references are not resolved to generated ids
19
+ - Stamps rows with `createdAt` (and `updatedAt` on main rows) set to the time of normalization
20
+
21
+ It does not deduplicate rows (two identical addresses produce two `addresses` rows), validate resources, or talk to a database.
15
22
 
16
23
  ## Supported FHIR Resources
17
24
 
18
- The library currently supports normalization of the following FHIR R4 resources:
19
-
20
- - **Patient**: Demographics, identifiers, names, contacts, addresses
21
- - **Practitioner**: Healthcare providers, qualifications, contact information
22
- - **Organization**: Healthcare organizations, types, contact details
23
- - **Encounter**: Healthcare encounters, participants, locations
24
- - **Condition**: Medical conditions, clinical status, evidence
25
- - **Observation**: Clinical measurements, vital signs, lab results
26
- - **MedicationRequest**: Medication prescriptions, dosage instructions
27
- - **Procedure**: Medical procedures, performers, outcomes
28
- - **Immunization**: Vaccinations, reactions, protocols
29
- - **AllergyIntolerance**: Allergies, reactions, manifestations
30
- - **DiagnosticReport**: Diagnostic test results, conclusions
31
- - **Location**: Healthcare facilities, positions, contact info
32
- - **Appointment**: Scheduled appointments, participants
33
- - **CareTeam**: Care team members, roles, periods
34
- - **CarePlan**: Care plans, activities, goals
25
+ Entries whose `resourceType` is one of the following are normalized; any other resource type only produces a `bundleEntries` row.
26
+
27
+ - **Patient**: demographics, identifiers, names, contacts (with their telecoms/address), addresses, telecoms, general practitioners, photos, marital status
28
+ - **Practitioner**: demographics, qualifications, identifiers, names, telecoms, addresses, photos, communication languages
29
+ - **Organization**: types, identifiers, telecoms, addresses, endpoints
30
+ - **Encounter**: class, status, period, participants, subject, identifiers, basedOn, appointments, reasons
31
+ - **Condition**: clinical/verification status, code, category, body site, notes
32
+ - **Observation**: code, value, components, category, interpretation, notes, performers, related references
33
+ - **MedicationRequest**: medication, dosage instructions (first dose/rate), category, reasons, notes, related references
34
+ - **Procedure**: performers, reasons, body site, complications, follow-up, notes, used items
35
+ - **Immunization**: vaccine, dose, site/route, reasons, subpotent reasons, program eligibility, notes
36
+ - **AllergyIntolerance**: reactions, manifestations, categories, notes
37
+ - **DiagnosticReport**: result references, category, performers, conclusion codes, presented forms
38
+ - **Location**: position, type, aliases, telecoms, address, endpoints
39
+ - **Appointment**: participants, reasons, slots, related references
40
+ - **CareTeam**: participants, category, reasons, managing organizations, telecoms, notes
41
+ - **CarePlan**: activities, category, goals, care teams and other references, notes
42
+
43
+ Sub-structures not listed (for example `Condition.evidence`, `Encounter.location`, `Immunization.reaction`/`protocolApplied`, `Patient.communication`) are typed but not normalized.
35
44
 
36
45
  ## Usage
37
46
 
38
47
  ```typescript
39
- import { normalizeFhirBundle } from '@onivoro/isomorphic-fhir';
48
+ import { normalizeFhirBundle, IFhirBundle } from '@onivoro/isomorphic-fhir';
40
49
 
41
- const fhirBundle = {
50
+ const fhirBundle: IFhirBundle = {
42
51
  resourceType: 'Bundle',
43
52
  type: 'searchset',
44
53
  entry: [
@@ -48,8 +57,8 @@ const fhirBundle = {
48
57
  id: 'patient-123',
49
58
  name: [{ family: 'Doe', given: ['John'] }],
50
59
  gender: 'male',
51
- birthDate: '1985-03-15'
52
- }
60
+ birthDate: '1985-03-15',
61
+ },
53
62
  },
54
63
  {
55
64
  resource: {
@@ -58,132 +67,88 @@ const fhirBundle = {
58
67
  status: 'final',
59
68
  code: { coding: [{ code: '8867-4', display: 'Heart rate' }] },
60
69
  subject: { reference: 'Patient/patient-123' },
61
- valueQuantity: { value: 72, unit: 'beats/min' }
62
- }
63
- }
64
- ]
70
+ value: { value: 72, unit: 'beats/min' },
71
+ },
72
+ },
73
+ ],
65
74
  };
66
75
 
67
76
  const normalizedData = normalizeFhirBundle(fhirBundle);
68
77
 
69
- // normalizedData contains normalized tables:
70
- console.log(normalizedData.patients); // Patient demographics
71
- console.log(normalizedData.patientNames); // Patient names (separate table)
72
- console.log(normalizedData.observations); // Clinical observations
73
- console.log(normalizedData.addresses); // Addresses (deduplicated)
74
- console.log(normalizedData.telecoms); // Contact points
75
- console.log(normalizedData.references); // Resource relationships
78
+ console.log(normalizedData.bundles); // one row for the bundle
79
+ console.log(normalizedData.patients); // patient demographics
80
+ console.log(normalizedData.patientNames); // patient names, linked by patientId
81
+ console.log(normalizedData.observations); // valueType 'Quantity', valueNumber 72, valueString 'beats/min'
82
+ ```
83
+
84
+ ### Choice-type (`[x]`) fields
85
+
86
+ The input interfaces model FHIR choice-type elements with a single un-suffixed property, and the normalizer only reads that property. Use `value`, `effective`, `onset`, `abatement`, `deceased`, `multipleBirth`, `medication`, `performed`, `occurrence`, `asNeeded` and `scheduled` rather than the wire-format names such as `valueQuantity`, `effectiveDateTime` or `medicationCodeableConcept`. Raw FHIR JSON from a server must be mapped to these names first, or those fields are left empty.
87
+
88
+ ## API
89
+
90
+ ### `normalizeFhirBundle(bundle: IFhirBundle): INormalizedFhirData`
91
+
92
+ Returns an `INormalizedFhirData` object with one array per table. Every array is always present (empty when there is nothing to put in it).
93
+
94
+ ### Types
95
+
96
+ - **Input interfaces** (`fhir-resources.interface`): `IFhirBundle`, `IFhirBundleEntry`, the `IFhirResource` union, one interface per supported resource (`IFhirPatient`, `IFhirPractitioner`, `IFhirOrganization`, `IFhirEncounter`, `IFhirCondition`, `IFhirObservation`, `IFhirMedicationRequest`, `IFhirProcedure`, `IFhirImmunization`, `IFhirAllergyIntolerance`, `IFhirDiagnosticReport`, `IFhirLocation`, `IFhirAppointment`, `IFhirCareTeam`, `IFhirCarePlan`), common data types (`IFhirIdentifier`, `IFhirHumanName`, `IFhirContactPoint`, `IFhirAddress`, `IFhirCodeableConcept`, `IFhirCoding`, `IFhirReference`, `IFhirPeriod`, `IFhirQuantity`, `IFhirRange`, `IFhirAge`, `IFhirAttachment`, `IFhirAnnotation`, `IFhirDuration`, `IFhirDateTime`, `IFhirDosage`, `IFhirTiming`, `IFhirTimingRepeat`, `IFhirRatio`, `IFhirDosageDoseAndRate`) and backbone elements (e.g. `IFhirPatientContact`, `IFhirEncounterParticipant`, `IFhirObservationComponent`, `IFhirCarePlanActivityDetail`).
97
+ - **Output interfaces** (`fhir-normalized-tables.interface`): `INormalizedFhirData` and one row interface per table, named `I<Table>Table` (e.g. `IPatientTable`, `IObservationComponentTable`, `ICodingTable`, `IReferenceTable`, `IBundleEntryTable`).
98
+
99
+ ```typescript
100
+ import { INormalizedFhirData, IPatientTable } from '@onivoro/isomorphic-fhir';
101
+
102
+ function patientsBornBefore(data: INormalizedFhirData, isoDate: string): IPatientTable[] {
103
+ return data.patients.filter((p) => p.birthDate && p.birthDate < isoDate);
104
+ }
76
105
  ```
77
106
 
78
107
  ## Normalized Tables Generated
79
108
 
80
- The library generates 30+ normalized tables following healthcare data patterns:
109
+ `INormalizedFhirData` has 39 tables.
81
110
 
82
111
  ### Core Resource Tables
83
- - **bundles**: FHIR bundle metadata
84
- - **bundleEntries**: Bundle entry information
85
- - **patients**: Patient demographics
86
- - **practitioners**: Healthcare provider information
87
- - **organizations**: Healthcare organizations
88
- - **encounters**: Healthcare encounters
89
- - **conditions**: Medical conditions and diagnoses
90
- - **observations**: Clinical measurements and findings
91
- - **medicationRequests**: Medication prescriptions
92
- - **procedures**: Medical procedures performed
93
- - **immunizations**: Vaccination records
94
- - **allergyIntolerances**: Allergy and intolerance information
95
- - **diagnosticReports**: Diagnostic test reports
96
- - **locations**: Healthcare facility information
97
- - **appointments**: Scheduled appointments
98
- - **careTeams**: Care team compositions
99
- - **carePlans**: Patient care plans
112
+
113
+ - **bundles**: bundle metadata (`type`, `timestamp`, `total`)
114
+ - **bundleEntries**: one row per entry (`fullUrl`, search mode/score, `resourceType`, `resourceId`)
115
+ - **patients**, **practitioners**, **organizations**, **encounters**, **conditions**, **observations**, **medicationRequests**, **procedures**, **immunizations**, **allergyIntolerances**, **diagnosticReports**, **locations**, **appointments**, **careTeams**, **carePlans**: one row per resource; coded fields are flattened to the first coding's code/display
100
116
 
101
117
  ### Relationship and Detail Tables
102
- - **patientIdentifiers**: Patient identifier systems
103
- - **patientNames**: Patient name variations
104
- - **patientContacts**: Emergency/family contacts
105
- - **practitionerQualifications**: Provider credentials
106
- - **organizationTypes**: Organization classifications
107
- - **encounterParticipants**: Encounter participants
108
- - **observationComponents**: Multi-component observations
109
- - **medicationDosages**: Medication dosing instructions
110
- - **procedurePerformers**: Procedure performers
111
- - **allergyReactions**: Allergic reactions
112
- - **allergyManifestations**: Reaction manifestations
113
- - **diagnosticReportResults**: Report result references
114
- - **appointmentParticipants**: Appointment participants
115
- - **careTeamParticipants**: Care team member details
116
- - **carePlanActivities**: Care plan activity details
118
+
119
+ - **patientIdentifiers**, **patientNames**, **patientContacts**
120
+ - **practitionerQualifications**
121
+ - **organizationTypes** (first coding of each type)
122
+ - **encounterParticipants**
123
+ - **observationComponents**
124
+ - **medicationDosages**
125
+ - **procedurePerformers**
126
+ - **allergyReactions**, **allergyManifestations**
127
+ - **diagnosticReportResults**
128
+ - **appointmentParticipants**
129
+ - **careTeamParticipants**
130
+ - **carePlanActivities**
117
131
 
118
132
  ### Common Data Type Tables
119
- - **addresses**: Physical addresses (deduplicated)
120
- - **telecoms**: Contact points (phone, email, etc.)
121
- - **codeableConcepts**: Coded concepts with text
122
- - **codings**: Individual coding systems
123
- - **notes**: Annotations and comments
124
- - **attachments**: Binary attachments and documents
125
- - **references**: Resource references and relationships
126
-
127
- ## Key Features
128
-
129
- ### FHIR Compliance
130
- - Full FHIR R4 resource support
131
- - Proper handling of FHIR data types (CodeableConcept, Reference, etc.)
132
- - Preservation of clinical terminology and coding systems
133
- - Support for complex nested structures
134
-
135
- ### Data Integrity
136
- - Referential integrity through normalized foreign keys
137
- - Unique constraint handling for identifiers
138
- - Proper temporal data handling (effective periods, timestamps)
139
- - Clinical status and verification status preservation
140
-
141
- ### Healthcare-Specific Patterns
142
- - Support for multiple identifier systems per resource
143
- - Proper handling of clinical hierarchies (conditions, observations)
144
- - Care team and care plan relationship modeling
145
- - Medication dosing and administration patterns
146
- - Allergy and adverse reaction tracking
147
-
148
- ### Performance Optimized
149
- - Efficient deduplication of common entities (addresses, codings)
150
- - Bulk insert-friendly table structures
151
- - Indexed foreign key relationships
152
- - Minimal data redundancy
153
133
 
154
- ## Testing
134
+ - **addresses**: addresses of any resource (`line` joined with `\n`)
135
+ - **telecoms**: contact points
136
+ - **codeableConcepts**: one row per CodeableConcept, with `fieldName` (e.g. `category`, `reasonCode`)
137
+ - **codings**: codings of each CodeableConcept, linked by `codeableConceptId`
138
+ - **notes**: annotations
139
+ - **attachments**: photos and presented forms
140
+ - **references**: references, keyed by `fieldName` (e.g. `subject`, `basedOn`, `performer`)
141
+
142
+ Identifiers of resources other than Patient are stored in **references** with `fieldName: 'identifier'` (the identifier's `type` goes to **codeableConcepts** as `identifier.type`). Practitioner names are stored as text in **codeableConcepts** with `fieldName: 'name'`: the name's `text`, or else its prefix, given, family and suffix parts joined with spaces, skipping missing parts. Patient contact names follow the same rule (`text`, or else given and family). The generic tables' `resourceId` is the generated row id of the owning record, not the FHIR id.
155
143
 
156
- The library includes comprehensive tests covering:
157
- - Complete FHIR bundle normalization
158
- - Individual resource type processing
159
- - Complex nested structure handling
160
- - Reference relationship preservation
161
- - Edge cases and error conditions
144
+ ## Testing
162
145
 
163
- Run tests with:
164
146
  ```bash
165
- nx test lib-isomorphic-fhir
147
+ npx nx test lib-isomorphic-fhir
166
148
  ```
167
149
 
168
- ## FHIR Standards Compliance
169
-
170
- This library is designed to work with FHIR R4 resources and follows HL7 FHIR standards for:
171
- - Resource structure and cardinality
172
- - Data type handling and validation
173
- - Reference resolution and integrity
174
- - Clinical terminology bindings
175
- - Healthcare workflow patterns
176
-
177
- ## Use Cases
178
-
179
- Perfect for:
180
- - **EHR Data Warehousing**: Normalize FHIR data for analytics
181
- - **Clinical Data Lakes**: Store FHIR resources in relational format
182
- - **Interoperability Solutions**: Transform FHIR for legacy systems
183
- - **Healthcare Analytics**: Enable SQL-based analysis of clinical data
184
- - **Compliance Reporting**: Structure data for regulatory requirements
185
- - **Research Databases**: Prepare clinical data for research queries
150
+ The project also contains integration specs that call the public HAPI FHIR server; see `INTEGRATION_TESTS.md` in the repository.
186
151
 
187
152
  ## License
188
153
 
189
- This library is licensed under the MIT License. See the LICENSE file in this package for details.
154
+ This library is licensed under the MIT License. See the LICENSE file in this package for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onivoro/isomorphic-fhir",
3
- "version": "24.38.2",
3
+ "version": "25.0.2",
4
4
  "license": "MIT",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -10,9 +10,8 @@
10
10
  "url": "https://github.com/onivoro/monorepo.git"
11
11
  },
12
12
  "dependencies": {
13
- "@nestjs/swagger": "*",
14
13
  "tslib": "^2.3.0",
15
- "uuid": "*"
14
+ "uuid": "^9.0.1"
16
15
  },
17
16
  "files": [
18
17
  "**/*.js",