CPIA Module 3, Section 5: Practical FHIR Lab – Building a Medication Request Resource
MODULE 3: STANDARDS & INTEROPERABILITY

Section 3.5: Practical FHIR Lab – Building a MedicationRequest Resource

A hands-on conclusion where we will construct a valid FHIR “MedicationRequest” resource from scratch, translating a standard prescription into a fully interoperable, API-ready data object.

SECTION 3.5

Practical FHIR Lab: Building a MedicationRequest

Translating a clinical concept into a structured, machine-readable data object.

3.5.1 The “Why”: From Abstract Concept to Concrete Skill

Throughout this module, we have explored the evolution of interoperability standards, moving from the cryptic syntax of HL7v2 to the document-centric model of CDA, and finally to the modern, web-native approach of FHIR. We have discussed FHIR in conceptual terms—as a collection of “Resources” that act like Lego blocks for healthcare data. This final section serves as the capstone for the entire module. We will move beyond theory and engage in a practical, hands-on exercise: building a valid FHIR resource from scratch.

Why is this skill so important? As a CPIA, you will not be a full-time software developer, but you must be fluent in the language of developers. You will be tasked with writing detailed specifications for new interfaces, troubleshooting data quality issues, and validating that a new system is sending and receiving data correctly. To do this effectively, you must be able to read and understand the structure of a FHIR resource as comfortably as you read a prescription. You need to know what elements are required, what terminologies are used, and how different clinical concepts are modeled as data.

This lab will demystify the process. We will take the most common clinical artifact in your professional life—a simple prescription—and meticulously translate it, element by element, into a valid FHIR MedicationRequest resource. By the end of this exercise, you will have a tangible understanding of how a familiar clinical concept is transformed into a structured, interoperable, API-ready data object. This is the fundamental skill that allows an informatics pharmacist to bridge the gap between the clinical world and the technical world.

The Lab Scenario: A Standard Prescription

Our goal is to create a FHIR resource that is the digital equivalent of the following common prescription. Every piece of information on this “digital prescription pad” will be mapped to a specific element in our final JSON object.

Dr. Bill T. Provider, MD (NPI: 445566)

Date: 2025-10-18

Patient: John J. Smith

DOB: 1965-04-01

MRN: 1234567

Lisinopril 10 mg Oral Tablet

Sig: Take one (1) tablet by mouth once daily.

Dispense: #30 (thirty)

Refills: 11 (eleven)

Retail Pharmacist Analogy: Learning the Prior Authorization Form

Remember the first time you had to fill out a complex, multi-page prior authorization form for a specialty medication? It was likely an intimidating document, filled with jargon, checkboxes, and fields with cryptic labels. You couldn’t just write a note on it; you had to learn the specific “rules” of the form to get the PA approved.

This lab is the exact same process. A FHIR MedicationRequest resource is simply a highly structured, standardized digital form. Our task is to learn the rules of this form.

  • Learning the Sections: First, you learned that the PA form was divided into logical sections: Patient Demographics, Prescriber Information, Medication Details, Clinical History, etc. In this lab, we will learn about the constituent FHIR resources that serve as these sections: `Patient`, `Practitioner`, and `MedicationRequest`.
  • Learning the Fields: Next, you learned what each specific box on the form was for. “Prescriber NPI” goes in one box, “Patient DOB” in another. We will do this by breaking down the “elements” (the JSON keys) of each FHIR resource.
  • Learning the Rules (Data Types & Value Sets): Most importantly, you learned the required format for each box. The date had to be `MM/DD/YYYY`. The diagnosis required a specific `ICD-10` code, not just a description. In FHIR, we will learn about data types (string, boolean, CodeableConcept) and “Value Sets” (the list of approved codes you can use for a specific element).

Just as mastering the PA form turned a chaotic process into a manageable workflow, mastering the structure of a FHIR resource will turn abstract data into a powerful tool for building safer and more efficient clinical systems.

3.5.2 The Building Blocks: Prerequisite Resources

A core principle of FHIR is that resources are modular and interconnected. A `MedicationRequest` does not contain all the patient’s demographic information and all the prescriber’s information within itself. That would be massively redundant. Instead, it simply points or references other, separate resources. Therefore, before we can build our `MedicationRequest`, we must first define the `Patient` and `Practitioner` resources that it will link to.

The `Patient` Resource

The `Patient` resource contains demographic information about a specific patient. For our scenario, we will create a resource for John J. Smith.

{
  "resourceType": "Patient",
  "id": "1234567",
  "identifier": [
    {
      "use": "usual",
      "type": {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
            "code": "MR",
            "display": "Medical Record Number"
          }
        ]
      },
      "system": "urn:oid:1.2.3.4.5.6.7",
      "value": "1234567"
    }
  ],
  "name": [
    {
      "use": "official",
      "family": "Smith",
      "given": [
        "John",
        "J."
      ]
    }
  ],
  "gender": "male",
  "birthDate": "1965-04-01"
}
Element-by-Element Breakdown: `Patient`
Element Name Data Type Cardinality Explanation & Our Value
resourceType String 1..1 (Required) Must always be present and state the type of the resource. For us, it’s “Patient”.
id Id 0..1 (Optional) The logical ID for this resource on the FHIR server. This is the ID that other resources will use to reference it. We’ve set it to the patient’s MRN for simplicity: “1234567”.
identifier Identifier 0..* (Optional, Multiple) A business identifier for the patient. This is where you put things like MRN, Social Security Number, etc. It’s an array to allow for multiple identifiers. We’ve included one entry for the MRN. Note the structure: it has a `type` (MR), a `system` (a unique identifier for the hospital assigning the MRN), and the `value`.
name HumanName 0..* The patient’s name. It’s an array to allow for official names, maiden names, nicknames, etc. We’ve defined the official name with a `family` name (“Smith”) and an array of `given` names (“John”, “J.”).
gender Code 0..1 The patient’s administrative gender. The value must come from a defined Value Set (`male`, `female`, `other`, `unknown`).
birthDate Date 0..1 The patient’s date of birth in YYYY-MM-DD format.

The `Practitioner` Resource

Similarly, the `Practitioner` resource contains information about a specific healthcare provider. We’ll create one for Dr. Bill T. Provider.

{
  "resourceType": "Practitioner",
  "id": "445566",
  "identifier": [
    {
      "system": "http://hl7.org/fhir/sid/us-npi",
      "value": "445566"
    }
  ],
  "name": [
    {
      "family": "Provider",
      "given": [
        "Bill",
        "T."
      ],
      "suffix": [
        "MD"
      ]
    }
  ]
}
Element-by-Element Breakdown: `Practitioner`
Element Name Data Type Cardinality Explanation & Our Value
resourceType String 1..1 Must be “Practitioner”.
id Id 0..1 The logical ID for this practitioner on the server. We are using their NPI for this: “445566”.
identifier Identifier 0..* Business identifiers for the provider. The most important one is the National Provider Identifier (NPI). The `system` URI for NPI is a standard value.
name HumanName 0..* The provider’s name, structured similarly to the Patient’s name, with `family` and `given` name parts.

3.5.3 The Main Event: Constructing the `MedicationRequest` Resource

With our prerequisite `Patient` and `Practitioner` resources defined, we are now ready to build the main event: the `MedicationRequest` resource. This resource is the FHIR equivalent of a prescription or a medication order. We will build it from the top down, explaining each element and its significance in detail.

Part 1: The `MedicationRequest` Shell (Metadata and Status)

We begin by defining the resource type and its core metadata, including its status and intent.

{
  "resourceType": "MedicationRequest",
  "id": "medrx001",
  "status": "active",
  "intent": "order",
  "authoredOn": "2025-10-18T12:00:00Z",
...
Element-by-Element Breakdown: Metadata
Element Name Explanation & Our Value
resourceType Required. Must be “MedicationRequest”.
id A server-specific logical ID for this prescription. We’ve assigned it “medrx001”.
status This required element indicates the current state of the request. “active” means it is a current, valid order. Other common values include “completed” (the course of therapy is finished), “stopped” (discontinued by a provider), and “cancelled” (voided before being dispensed).
intent This required element describes the author’s intention. It is critical for distinguishing between different types of requests. The most common values are:
  • order: A directive to dispense and/or administer. This is a “real” prescription. This is our value.
  • plan: A record of a proposed or intended therapy that is NOT yet active. This is used for discharge medication lists, future orders, or therapy proposals.
  • proposal: A suggestion for a medication to be prescribed.
authoredOn The date and time the prescription was written. It should be in ISO 8601 format, including the time and timezone offset (the “Z” stands for Zulu/UTC time).

Part 2: Linking the Actors (`subject` and `requester`)

Next, we link our `MedicationRequest` to the patient it is for and the practitioner who wrote it. This is done via simple references.

...
  "subject": {
    "reference": "Patient/1234567",
    "display": "John J. Smith"
  },
  "requester": {
    "reference": "Practitioner/445566",
    "display": "Dr. Bill T. Provider, MD"
  },
...
Element-by-Element Breakdown: References
Element Name Explanation & Our Value
subject A required reference to the `Patient` or `Group` the medication is for. The `reference` element contains the “URL” to the patient resource on the FHIR server: `”Patient/[id]”`. We use the ID `1234567` from the `Patient` resource we defined earlier. The `display` element is optional human-readable text.
requester A reference to the `Practitioner` (or other valid resource) who wrote the order. The structure is the same: `”Practitioner/[id]”`. We use the ID `445566` from our `Practitioner` resource.

Part 3: Identifying the Drug (`medicationCodeableConcept`)

This is one of the most critical parts of the resource. We must unambiguously identify the exact medication being ordered. In FHIR, the preferred way to do this is with a medicationCodeableConcept, which allows us to use a standard terminology code.

...
  "medicationCodeableConcept": {
    "coding": [
      {
        "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
        "code": "216215",
        "display": "Lisinopril 10 MG Oral Tablet"
      }
    ],
    "text": "Lisinopril 10 MG Oral Tablet"
  },
...
Masterclass: The CodeableConcept Data Type

You will see this data type everywhere in FHIR. It’s the standard way to represent a concept that can be defined by a code. It has two main parts:

  • coding: An array of one or more coded representations. Each entry in the array has:
    • system: A unique URL (a URI) that identifies the coding system being used (e.g., RxNorm, SNOMED, LOINC, ICD-10).
    • code: The actual code for the concept from that system.
    • display: The human-readable name for that code.
  • text: The original text as entered by the user. This is a fallback in case the receiving system doesn’t understand any of the provided codes.

For medications in the US, the standard `system` is RxNorm. The RxNorm code `216215` specifically and unambiguously refers to the clinical drug product “Lisinopril 10 MG Oral Tablet”.

Part 4: The SIG (`dosageInstruction`)

Now we must translate “Take one (1) tablet by mouth once daily” into a structured format. This is handled by the dosageInstruction element, which is an array to accommodate complex, multi-part instructions (like tapers).

...
  "dosageInstruction": [
    {
      "text": "Take one (1) tablet by mouth once daily",
      "timing": {
        "repeat": {
          "frequency": 1,
          "period": 1,
          "periodUnit": "d"
        }
      },
      "route": {
        "coding": [
          {
            "system": "http://snomed.info/sct",
            "code": "26643006",
            "display": "Oral route"
          }
        ]
      },
      "doseAndRate": [
        {
          "type": { "text": "Ordered" },
          "doseQuantity": {
            "value": 1,
            "unit": "tablet",
            "system": "http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm"
          }
        }
      ]
    }
  ],
...
Element-by-Element Breakdown: `dosageInstruction`
Element Name Explanation & Our Value
text The full, human-readable sig string.
timing A complex element for describing the schedule. For a simple frequency, we use the `repeat` sub-element. The combination of `frequency: 1`, `period: 1`, and `periodUnit: “d”` is the structured way of saying “1 time per 1 day” or “once daily”.
route A `CodeableConcept` specifying the route of administration. The standard terminology for this is often SNOMED CT. The code `26643006` represents “Oral route”.
doseAndRate An array to specify the dose. For a simple dose, we use `doseQuantity`. Here, we specify the `value` is `1` and the `unit` is `tablet`. The `system` points to a standard FHIR terminology for dosage forms.

Part 5: Pharmacy Instructions (`dispenseRequest`)

Finally, we need to include the instructions for the dispensing pharmacy: the quantity and number of refills.

...
  "dispenseRequest": {
    "quantity": {
      "value": 30,
      "unit": "tablet",
      "system": "http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm"
    },
    "expectedSupplyDuration": {
      "value": 30,
      "unit": "days",
      "system": "http://unitsofmeasure.org"
    },
    "numberOfRepeatsAllowed": 11
  }
}
Element-by-Element Breakdown: `dispenseRequest`
Element Name Explanation & Our Value
quantity A `SimpleQuantity` data type indicating the amount to be dispensed. We specify `30` tablets.
expectedSupplyDuration The days’ supply. A `Duration` data type. We specify `30` days.
numberOfRepeatsAllowed An integer representing the number of refills allowed. For us, this is `11`.

3.5.4 Putting It All Together: The Final FHIR Resource

We have now defined all the necessary components. By assembling all the parts we’ve analyzed, we arrive at our final, complete, and valid FHIR `MedicationRequest` resource. This JSON object is the structured, machine-readable, and fully interoperable equivalent of the simple paper prescription we started with.

{
  "resourceType": "MedicationRequest",
  "id": "medrx001",
  "status": "active",
  "intent": "order",
  "medicationCodeableConcept": {
    "coding": [
      {
        "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
        "code": "216215",
        "display": "Lisinopril 10 MG Oral Tablet"
      }
    ],
    "text": "Lisinopril 10 MG Oral Tablet"
  },
  "subject": {
    "reference": "Patient/1234567",
    "display": "John J. Smith"
  },
  "authoredOn": "2025-10-18T12:00:00Z",
  "requester": {
    "reference": "Practitioner/445566",
    "display": "Dr. Bill T. Provider, MD"
  },
  "dosageInstruction": [
    {
      "text": "Take one (1) tablet by mouth once daily",
      "timing": {
        "repeat": {
          "frequency": 1,
          "period": 1,
          "periodUnit": "d"
        }
      },
      "route": {
        "coding": [
          {
            "system": "http://snomed.info/sct",
            "code": "26643006",
            "display": "Oral route"
          }
        ]
      },
      "doseAndRate": [
        {
          "type": { "text": "Ordered" },
          "doseQuantity": {
            "value": 1,
            "unit": "tablet",
            "system": "http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm"
          }
        }
      ]
    }
  ],
  "dispenseRequest": {
    "quantity": {
      "value": 30,
      "unit": "tablet",
      "system": "http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm"
    },
    "expectedSupplyDuration": {
      "value": 30,
      "unit": "days",
      "system": "http://unitsofmeasure.org"
    },
    "numberOfRepeatsAllowed": 11
  }
}