Converting Data to FHIR | Medplum

On this page

When migrating data from a 3rd party system to Medplum, a crucial step is converting your existing data into FHIR-compliant resources. This section will guide you through the process of reshaping your data elements, adding identifiers, dealing with codeable concepts, and linking data using conditional references.

Reshaping Data Elements from the Source

When converting your data to FHIR resources, you'll need to map your existing data elements to the appropriate FHIR resource fields. This process involves:

  1. Identifying the corresponding FHIR resource type for each data entity in your source system.
  2. Mapping individual data fields to FHIR resource attributes.
  3. Transforming data formats to match FHIR requirements (e.g., date formats, name structures).

Here's a simple example of how you might map fields from a source system to FHIR:

Source System (Patient table):

| id   | first_name | last_name | dob        | gender |
| ---- | ---------- | --------- | ---------- | ------ |
| P001 | John       | Doe       | 1980-05-15 | M      |

FHIR Patient Resource:

{
  resourceType: 'Patient',
  name: [
    {
      given: ['John'],
      family: 'Doe',
    },
  ],
  birthDate: '1980-05-15',
  gender: 'male',
};

Using FHIR Identifiers to link to the Source System

To maintain traceability and link back to your source system, it's crucial to add the primary keys from your source system as identifiers in Medplum.

Adding source system identifiers helps in:

Example

{
  resourceType: 'Patient',
  identifier: [
    {
      system: 'http://your-source-system.com/patientId',
      value: 'P001',
    },
  ],
  // ... other patient data
};

Dealing with CodeableConcepts

As mentioned in FHIR Basics, annotating your data with standardized codes is crucial for interoperability with the healthcare ecosystem. When converting your data to FHIR, you'll find that many target elements have the type CodeableConcept.

It's great if your source data is already annotated with standard codes. If not, you can:

  1. Add a local code representing your internal coding scheme
  2. Enrich your data with standard codes in a separate process

Example

Let's look at an example of converting condition data to a FHIR Condition resource. First, we'll show what the existing data might look like in a tabular form:

Conditions Table:

| condition_id | condition_name |
| ------------ | -------------- |
| HT001        | Hypertension   |
| DM002        | Diabetes       |

Patient_Conditions Table:

| patient_condition_id | patient_id | condition_id |
| -------------------- | ---------- | ------------ |
| PC001                | P001       | HT001        |
| PC002                | P001       | DM002        |
| PC003                | P002       | HT001        |

Now, let's convert this data to a FHIR Condition resource:

Initially, with just the local code:

{
  resourceType: 'Condition',
  identifier: [
    {
      system: 'http://your-source-system.com/patient_conditions',
      value: 'PC001',
    },
  ],
  subject: {
    reference: 'Patient/????',
  },
  code: {
    coding: [
      {
        system: 'http://your-source-system.com/conditions',
        code: 'HT001',
        display: 'Hypertension',
      },
    ],
    text: 'Hypertension',
  },
};

Later, enriched with a standard ICD-10 code:

resourceType: 'Condition',
identifier: [
  {
    system: 'http://your-source-system.com/patient_conditions',
    value: 'PC001',
  },
],
subject: {
  reference: 'Patient?identifier=http://your-source-system.com/patients|P001',
},
code: {
  coding: [
    {
      system: 'http://your-source-system.com/conditions',
      code: 'HT001',
      display: 'Hypertension',
    },
    {
      system: 'http://hl7.org/fhir/sid/icd-10',
      code: 'I10',
      display: 'Essential (primary) hypertension',
    },
  ],
  text: 'Hypertension',
},
onsetDateTime: '2022-03-15',

In this example:

  1. We start with local codes from the source system, allowing us to maintain traceability back to the original data.
  2. We later augment the data with a standard ICD-10 code improving interoperability with other systems that understand ICD-10 codes.
  3. The text field provides a human-readable description of the condition.

When implementing this in your migration pipeline:

This approach allows you to migrate your data quickly while still maintaining the ability to add standardized coding later, improving the overall quality and interoperability of your data over time.

Linking Data Using Conditional References

When migrating data to FHIR, it's crucial to maintain relationships between resources. Conditional references are particularly helpful in this process.

During migration, it's often challenging to know the unique id that Medplum will assign to a resource once it's created. You may have noticed the reference to Patient/???? in the previous example.

Conditional references solve these issues by allowing you to reference resources based on their identifying information from the source system, rather than relying on Medplum-generated IDs.

Example

Let's amend the previous example to use a conditional reference to link our Condition to the Patient.

{
  resourceType: 'Condition',
  identifier: [
    {
      system: 'http://your-source-system.com/patient_conditions',
      value: 'PC001',
    },
  ],
  subject: {
    reference: 'Patient?identifier=http://your-source-system.com/patients|P001',
  },
};

With this modification, we no longer have to look up the Patient'sid value in Medplum before writing the Condition. The server will automatically resolve the query string identifier=http://your-source-system.com/patients|P001 into the concrete patient id during the write.

An End-to-End Example

Let's look at a complete example that demonstrates converting tabular data into FHIR resources, including patients and conditions, using local codes, standard codes, and conditional references.

Source Data

First, let's look at our source data in tabular form:

Patients Table:

| patient_id | first_name | last_name | birth_date | gender |
| ---------- | ---------- | --------- | ---------- | ------ |
| P001       | John       | Doe       | 1980-07-15 | M      |
| P002       | Jane       | Smith     | 1992-11-30 | F      |

Conditions Table:

| condition_id | condition_name | icd10_code |
| ------------ | -------------- | ---------- |
| HT001        | Hypertension   | I10        |
| DM002        | Diabetes       | E11        |

Patient_Conditions Table:

| patient_condition_id | patient_id | condition_id | onset_date |
| -------------------- | ---------- | ------------ | ---------- |
| PC001                | P001       | HT001        | 2022-03-15 |
| PC002                | P001       | DM002        | 2023-01-10 |
| PC003                | P002       | HT001        | 2023-02-22 |

Resulting FHIR Resources

Now, let's look at how this data would be represented as FHIR resources:

Patient Resources

For John Doe (P001):

{
  resourceType: 'Patient',
  identifier: [
    {
      system: 'http://your-source-system.com/patients',
      value: 'P001',
    },
  ],
  name: [
    {
      given: ['John'],
      family: 'Doe',
    },
  ],
  birthDate: '1980-07-15',
  gender: 'male',
};

For Jane Smith (P002):

{
  resourceType: 'Patient',
  identifier: [
    {
      system: 'http://your-source-system.com/patients',
      value: 'P002',
    },
  ],
  name: [
    {
      given: ['Jane'],
      family: 'Smith',
    },
  ],
  birthDate: '1992-11-30',
  gender: 'female',
};
Condition Resources:

For John Doe's Hypertension (PC001):

{
  resourceType: 'Condition',
  identifier: [
    {
      system: 'http://your-source-system.com/patient_conditions',
      value: 'PC001',
    },
  ],
  subject: {
    reference: 'Patient?identifier=http://your-source-system.com/patients|P001',
  },
  code: {
    coding: [
      {
        system: 'http://your-source-system.com/conditions',
        code: 'HT001',
        display: 'Hypertension',
      },
      {
        system: 'http://hl7.org/fhir/sid/icd-10',
        code: 'I10',
        display: 'Essential (primary) hypertension',
      },
    ],
    text: 'Hypertension',
  },
  onsetDateTime: '2022-03-15',
};

For John Doe's Diabetes (PC002):

{
  resourceType: 'Condition',
  identifier: [
    {
      system: 'http://your-source-system.com/patient_conditions',
      value: 'PC002',
    },
  ],
  subject: {
    reference: 'Patient?identifier=http://your-source-system.com/patients|P001',
  },
  code: {
    coding: [
      {
        system: 'http://your-source-system.com/conditions',
        code: 'DM002',
        display: 'Diabetes',
      },
      {
        system: 'http://hl7.org/fhir/sid/icd-10',
        code: 'E11',
        display: 'Type 2 diabetes mellitus',
      },
    ],
    text: 'Diabetes',
  },
  onsetDateTime: '2023-01-10',
};

For Jane Smith's Hypertension (PC003):

{
  resourceType: 'Condition',
  identifier: [
    {
      system: 'http://your-source-system.com/patient_conditions',
      value: 'PC003',
    },
  ],
  subject: {
    reference: 'Patient?identifier=http://your-source-system.com/patients|P002',
  },
  code: {
    coding: [
      {
        system: 'http://your-source-system.com/conditions',
        code: 'HT001',
        display: 'Hypertension',
      },
      {
        system: 'http://hl7.org/fhir/sid/icd-10',
        code: 'I10',
        display: 'Essential (primary) hypertension',
      },
    ],
    text: 'Hypertension',
  },
  onsetDateTime: '2023-02-22',
};

This example demonstrates:

Conclusion

By following this pattern, you can extend the migration process to handle more complex data structures and relationships while maintaining the integrity and traceability of your source data.