Healthcare interoperability has historically relied on HL7 v2, a messaging standard created in the 1980s. These messages are compact, pipe-delimited, and event-driven (e.g., Admit, Discharge, Transfer events). But with the shift toward mobile apps, cloud platforms, and modern REST APIs, FHIR (Fast Healthcare Interoperability Resources) has become the go-to standard for exchanging healthcare data in a structured, API-first way.
Bridging HL7 v2 to FHIR conversion is critical for hospitals, EHR vendors, and startups that need to support legacy systems while exposing modern APIs.
This guide explores the why, how, and practical details of HL7 v2 to FHIR conversion, with a TypeScript-based code example for implementers.
Why Convert HL7 v2 to FHIR?
HL7 v2 to FHIR conversion enables organizations to modernize healthcare data exchange while maintaining backward compatibility.
- Legacy compatibility: Many hospitals still rely on HL7 v2 feeds (ADT, ORU, ORM, SIU).
- Modernization: Expose data as FHIR APIs for mobile apps, analytics, and cloud workflows.
- Standards compliance: Regulatory pushes (like the US ONC Cures Act) favor FHIR APIs.
- Interoperability: Vendors can integrate across systems more easily with FHIR JSON.
HL7 v2 Basics
Example ADT^A01 (Admit/Visit Notification) message:
MSH|^~\&|HOSP|HOSPIS|EHR|EHR|20250924||ADT^A01|MSG00001|P|2.5
EVN|A01|20250924
PID|1|12345^^^HOSP^MR||DOE^JOHN^A||19800101|M||2106-3|123 MAIN ST^^MUMBAI^MH^400001|MH|(999)999-9999|||S||123456789|987-65-4321Key components:
- MSH (Message Header): Message metadata (event type, sending/receiving apps).
- EVN (Event): Event code and timestamp.
- PID (Patient Identification): Patient demographics.
FHIR Basics
FHIR resources are structured JSON/XML objects with standardized schemas.
For example, during HL7 v2 to FHIR conversion, the HL7 v2 PID segment maps to a FHIR Patient resource.
Example FHIR Patient JSON:
{
"resourceType": "Patient",
"identifier": [
{ "system": "urn:oid:1.2.3.4.5.6.7.8", "value": "12345" }
],
"name": [
{ "family": "DOE", "given": ["JOHN", "A"] }
],
"gender": "male",
"birthDate": "1980-01-01",
"address": [
{ "line": ["123 MAIN ST"], "city": "MUMBAI", "state": "MH", "postalCode": "400001" }
]
}Common Mapping Patterns
Typical mappings in HL7 v2 to FHIR conversion include:
- PID → Patient
- PV1 → Encounter
- OBR → DiagnosticReport
- OBX → Observation
- ORC / RXA → Medication Request / Medication Administration
Challenges & Gotchas
Implementing HL7 v2 to FHIR conversion requires attention to key technical differences:
- Event-driven vs. stateful data: HL7 v2 events (e.g., Admit) must be translated into FHIR resources with create/update logic.
- Identifiers & duplicates: Different hospitals may use different identifier systems—use both system and value.
- Terminology: HL7 v2 sometimes uses local codes; map them to standard FHIR codes (SNOMED, LOINC, ICD).
- Custom Z-segments: Decide how to represent Z-segments in FHIR (extensions or ignore).
- Date/time formats: HL7 v2 uses YYYY-MM-DD; FHIR expects ISO8601.
Ready to Build Your Own HL7 v2 to FHIR Pipeline?
Detailed TypeScript Example
Below is a TypeScript implementation that demonstrates the HL7 v2 to FHIR conversion process for a Patient resource.
Setup
npm install hl7-standard fhir-kit-client date-fnsConversion Script
import { parseHL7Message, Segment } from 'hl7-standard';
import { parse } from 'date-fns';
// Define minimal FHIR types for Patient (could also use @types/fhir if needed)
interface Identifier {
system: string;
value: string;
}
interface HumanName {
family?: string;
given?: string[];
}
interface Address {
line?: string[];
city?: string;
state?: string;
postalCode?: string;
}
interface Patient {
resourceType: 'Patient';
identifier?: Identifier[];
name?: HumanName[];
gender?: string;
birthDate?: string;
address?: Address[];
}
// Sample HL7 message
const hl7Message = `MSH|^~\\&|HOSP|HOSPIS|EHR|EHR|20250924||ADT^A01|MSG00001|P|2.5\nEVN|A01|20250924\nPID|1|12345^^^HOSP^MR||DOE^JOHN^A||19800101|M||2106-3|123 MAIN ST^^MUMBAI^MH^400001|MH|(999)999-9999|||S||123456789|987-65-4321`;
const msg = parseHL7Message(hl7Message);
const pid: Segment = msg.getSegment('PID');
const patient: Patient = { resourceType: 'Patient' };
// Identifier (MRN)
const mrn = pid.getField(3)?.toString();
if (mrn) {
patient.identifier = [{ system: 'urn:oid:1.2.3.4.5.6.7.8', value: mrn }];
}
// Name
const nameRaw = pid.getField(5)?.toString();
if (nameRaw) {
const parts = nameRaw.split('^');
patient.name = [{ family: parts[0], given: [parts[1], parts[2]].filter(Boolean) }];
}
// Gender
const gender = pid.getField(8)?.toString();
const gmap: Record<string, string> = { M: 'male', F: 'female', O: 'other', U: 'unknown' };
patient.gender = gender ? gmap[gender.toUpperCase()] : 'unknown';
// Birthdate
const dob = pid.getField(7)?.toString();
if (dob) patient.birthDate = parse(dob, 'yyyyMMdd', new Date()).toISOString().split('T')[0];
// Address
const addrRaw = pid.getField(11)?.toString();
if (addrRaw) {
const parts = addrRaw.split('^');
patient.address = [{ line: [parts[0]], city: parts[2], state: parts[3], postalCode: parts[4] }];
}
console.log(JSON.stringify(patient, null, 2));Output
{
"resourceType": "Patient",
"identifier": [
{ "system": "urn:oid:1.2.3.4.5.6.7.8", "value": "12345" }
],
"name": [
{ "family": "DOE", "given": ["JOHN", "A"] }
],
"gender": "male",
"birthDate": "1980-01-01",
"address": [
{ "line": ["123 MAIN ST"], "city": "MUMBAI", "state": "MH", "postalCode": "400001" }
]
}Error Handling & Logging
For reliable HL7 v2 to FHIR conversion, include:
- Validation: Use FHIR JSON schemas to validate converted resources.
- Logging: Always log the original HL7 v2 MSH.10 alongside FHIR resource IDs.
- Error resilience: For missing or invalid fields, decide whether to drop, default, or log.
Deployment Considerations
When scaling HL7 v2 to FHIR conversion pipelines:
- Streaming ingestion: Use Kafka/NATS for real-time ingestion and FHIR JSON downstream.
- Batch ETL: Run nightly conversions for bulk data in data lakes.
- Persistence: Store data in a FHIR server (e.g., HAPI-FHIR, Google Cloud Healthcare API).
- Extensibility: Add more HL7 v2 segments incrementally.
Best Practices
Adopt these best practices for efficient HL7 v2 to FHIR conversion:
- Keep mappings externalized (YAML/JSON).
- Start with ADT feed (PID + PV1), then expand.
- Use terminology servers for local code mapping.
- Handle versioning across HL7 v2.x standards.

Conclusion
HL7 v2 to FHIR conversion is not just a technical mapping exercise—it’s a strategic bridge between legacy systems and modern healthcare ecosystems. By carefully mapping, validating, and deploying a robust transformation pipeline, organizations can modernize their interoperability stack while ensuring compliance and future readiness.









BLOGS
NEWSROOM
CASE STUDIES
WEBINARS
PODCASTS
ASSET HUB
EVENT CALENDAR 





















