How to Enable FHIR Profile Validation with HAPI FHIR JPA Server

Sidharth Ramesh

Sidharth Ramesh

Founder and CEO

I recently had one of my students in our FHIR Bootcamp struggle with setting up HAPI FHIR so that when they post a FHIR Resource that belongs to a certain profile, it validates it before committing it to the server.

For example, if you make a POST request to /Patient with:

{
    "meta": {
        "profile": [
        "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient"
        ]
    },
    "resourceType": "Patient"
}

You'd expect that the server validates against the https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient profile automatically - because it's a URL on the internet. Right?!

Well, not really.

The above request just creates the resource by default, even though the identifier is mandatory.

identifier is mandatory

We'll walk through how you can configure HAPI FHIR to do profile validation for you, and also cover a few fundamental concepts of FHIR Profiles so you don't get stuck.

Setting up HAPI FHIR to validate FHIR resources against profiles

We're using v7.4.0 of HAPI FHIR here. The following environment variables should be set on the runtime running HAPI FHIR:

HAPI_FHIR_VALIDATION_REQUESTS_ENABLED=true
HAPI_FHIR_SERVER_VALIDATION_FLAG_FAIL_ON_SEVERITY=error
HAPI_FHIR_SERVER_VALIDATION_FLAG_ENFORCE_REQUIREMENT=true

If you're using docker-compose, you can use this docker-compose.yml

services:
  hapi-fhir-server:
    image: hapiproject/hapi:v7.4.0-tomcat
    container_name: hapi-fhir-server
    ports:
      - 8080:8080
    environment:
      HAPI_FHIR_VALIDATION_REQUESTS_ENABLED: 'true'
      HAPI_FHIR_SERVER_VALIDATION_FLAG_FAIL_ON_SEVERITY: error
      HAPI_FHIR_SERVER_VALIDATION_FLAG_ENFORCE_REQUIREMENT: 'true'

Posting StructureDefinition Resources on your FHIR Server

When posting a FHIR Resource with meta.profile set to https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient or http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient, the FHIR server does know what this means!

This is because although it looks like a URL, according to the FHIR specification, this is just a URI (Uniform Resource Identifier) that acts as a "global identifier".

URL is just an identifier

⚠️I repeat: The url field which a FHIR StructureDefinition (profile) is referenced by is not always a RESOLVABLE URL. It acts more as a global identifier.

So we need to manually upload the StructureDefinition resource to your FHIR Server first.

You can use the StructureDefinition for ABDM FHIR Patient Profile.

POST /StructureDefinition

{
"resourceType": "StructureDefinition",
...grab the rest from the link
}

Or if you're interested in creating your own profile's StructureDefinition, check out this video:

Once you've done this, now you can actually post the FHIR resource against a profile, and the HAPI FHIR Server will validate it for you.

Test it!

POST /Patient

{
    "meta": {
        "profile": [
        "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient"
        ]
    },
    "resourceType": "Patient"
}

will now return:

{
    "resourceType": "OperationOutcome",
    "issue": [
        {
            "extension": [
                {
                    "url": "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line",
                    "valueInteger": 1
                },
                {
                    "url": "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col",
                    "valueInteger": 2
                },
                {
                    "url": "http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id",
                    "valueString": "Validation_VAL_Profile_Minimum"
                }
            ],
            "severity": "error",
            "code": "processing",
            "details": {
                "coding": [
                    {
                        "system": "http://hl7.org/fhir/java-core-messageId",
                        "code": "Validation_VAL_Profile_Minimum"
                    }
                ]
            },
            "diagnostics": "Patient.identifier: minimum required = 1, but only found 0 (from https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient|6.0.0)",
            "location": [
                "Patient",
                "Line[1] Col[2]"
            ]
        }
    ]
}

as it should. Because identifier is now mandated by the profile (1..1). Look at our guide on cardinality.

Let's fix that error:

POST /Patient

{
    "meta": {
        "profile": [
            "https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient"
        ]
    },
    "resourceType": "Patient",
    "identifier": [
        {
            "type": {
                "coding": [
                    {
                        "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
                        "code": "MR",
                        "display": "Medical record number"
                    }
                ]
            },
            "system": "https://healthid.ndhm.gov.in",
            "value": "22-7225-4829-5255"
        }
    ]
}

and voila! It works!

Special thanks to Chaya and Arun.

Save lives with digital healthcare innovation
© 2024 Mediblocks. All rights reserved.