Custom resources using StructureDefinition

Custom resources are not interoperable with FHIR.

FHIR uses StructureDefinition resources to model data structures. Aidbox allows you to model data in the same way by expressing your custom resource via a FHIR StructureDefinition resource.

Configure Aidbox

To begin using custom FHIR resources, enable the FHIR Schema validator engine in Aidbox.

Setup Aidbox with FHIR Schema validation engine

Limitations of FHIR Logical Models

Logical models (StructureDefinition/FHIRSchema with kind: logical) have two limitations as guided by the FHIR specification:

  1. Base Value:

    • The base value is limited to two possible values: Element or base.

  2. Type Value:

    • The type value must be a fully specified URL and may share the same value as the URL property. This requirement is enforced by FHIR to avoid clashes with core resources.

Due to the second limitation, resource definitions based on logical models are not intended for instantiation and are provided to an end FHIR server only as data structure examples.

Create StructureDefinition for custom resource

To create a custom resource in Aidbox using StructureDefinition you have to create StructureDefinition resource via REST API.

It is a usual FHIR StructureDefinition resource, but with several limitations for creating a new custom resource:

  1. name, type, and id must be equal.

  2. kind: must be equal to resource or logical

  3. derivation: If it is set to specialization - Aidbox will create a new resource type with tables in the database and other resource infrastructure. If it is set to constraint - Aidbox will create a new profile that can be referenced on resource instances.

The further guide details the process of creating custom resources for a notification system, demonstrating the typical workflow of creating, managing, and sending template-based notifications from a healthcare system to patients using custom resources defined through StructureDefinition.

To implement a notification flow, you may need a notification resource and a template resource to store your notification messages.

Let's start with shaping a TutorNotificationTemplate resource.

This resource contains one property defined under StructureDefinition.differential:

  1. template: This property is for notification template text and is of the FHIR string data type. Also, it is a required property.

POST /fhir/StructureDefinition
content-type: application/json
accept: application/json

{
    "derivation": "specialization",
    "name": "TutorNotificationTemplate",
    "abstract": false,
    "type": "TutorNotificationTemplate",
    "status": "active",
    "kind": "resource",
    "url": "http://example.org/StructureDefinition/TutorNotificationTemplate",
    "baseDefinition": "http://hl7.org/fhir/StructureDefinition/DomainResource",
    "differential": {
        "element": [
            {
                "id": "TutorNotificationTemplate",
                "path": "TutorNotificationTemplate",
                "min": 0,
                "max": "*"
            },
            {
                "id": "TutorNotificationTemplate.template",
                "path": "TutorNotificationTemplate.template",
                "min": 1,
                "max": "1",
                "type": [
                    {
                        "code": "string"
                    }
                ]
            }
        ]
    }
}

Now, when we got resource to store our templates, let's shape more complex one - resource TutorNotification that has following properties:

  1. type: property that contains binding value set URL in valueSet property and strength: required, that is used to force binding validation.

  2. status: property with binding to valueSet: http://hl7.org/fhir/ValueSet/task-status with additional constraint to requested, in-progress or completed values.

  3. template: reference to TutorNotificationTemplate that we created above.

  4. message: message text and is of the FHIR string data type.

  5. sendAfter: property that specifies the dateTime after which this notification should be sent.

  6. subject: reference to the Patient resource to whom this notification will be sent.

POST /fhir/StructureDefinition
content-type: application/json
accept: application/json

{
  "url": "http://example.com/aidbox-sms-tutor/TutorNotification",
  "name": "TutorNotification",
  "status": "active",
  "kind": "resource",
  "abstract": false,
  "type": "TutorNotification",
  "baseDefinition": "http://hl7.org/fhir/StructureDefinition/DomainResource",
  "derivation": "specialization",
  "differential": {
    "element": [
      {
        "id": "TutorNotification",
        "path": "TutorNotification",
        "min": 0,
        "max": "*"
      },
      {
        "id": "TutorNotification.type",
        "path": "TutorNotification.type",
        "min": 1,
        "max": "1",
        "type": [
          {
            "code": "string"
          }
        ],
        "binding": {
          "strength": "required",
          "valueSet": "http://hl7.org/fhir/ValueSet/contact-point-system"
        }
      },
      {
        "id": "TutorNotification.status",
        "path": "TutorNotification.status",
        "min": 1,
        "max": "1",
        "type": [
          {
            "code": "string"
          }
        ],
        "constraint": [
          {
            "key": "cont-status",
            "severity": "error",
            "human": "Status should be 'requested', 'in-progress' or 'completed'",
            "expression": "%context='requested' or %context='in-progress' or %context='completed'"
          }
        ],
        "binding": {
          "strength": "required",
          "valueSet": "http://hl7.org/fhir/ValueSet/task-status"
        }
      },
      {
        "id": "TutorNotification.template",
        "path": "TutorNotification.template",
        "min": 1,
        "max": "1",
        "type": [
          {
            "code": "Reference",
            "targetProfile": [
              "http://example.com/aidbox-sms-tutor/TutorNotificationTemplate"
            ]
          }
        ]
      },
      {
        "id": "TutorNotification.message",
        "path": "TutorNotification.message",
        "min": 0,
        "max": "1",
        "type": [
          {
            "code": "string"
          }
        ]
      },
      {
        "id": "TutorNotification.sendAfter",
        "path": "TutorNotification.sendAfter",
        "min": 1,
        "max": "1",
        "type": [
          {
            "code": "dateTime"
          }
        ]
      },
      {
        "id": "TutorNotification.subject",
        "path": "TutorNotification.subject",
        "min": 1,
        "max": "1",
        "type": [
          {
            "code": "Reference",
            "targetProfile": [
              "http://hl7.org/fhir/StructureDefinition/Patient"
            ]
          }
        ]
      }
    ]
  }
}

Create Search Parameters on custom resources

POST /fhir/TutorNotification
content-type: application/json
accept: application/json

{
  "resourceType": "TutorNotification",
  "type": "sms",
  "status": "requested",
  "template": {
    "reference": "TutorNotificationTemplate/welcome"
  },
  "sendAfter": "2024-07-12T12:00:00.000Z",
  "subject": {
    "reference": "Patient/pt-1"
  }
}

So request that creates welcome sms notification for James Morgan at 12:00 should look like this:

POST /fhir/Patient
content-type: application/json
accept: application/json

{
  "id": "pt-1",
  "name": [
    {
      "given": [
        "James"
      ],
      "family": "Morgan"
    }
  ],
  "resourceType": "Patient"
}

Then we probably want to create some patient:

POST /fhir/TutorNotificationTemplate
content-type: application/json
accept: application/json

{
  "id": "welcome",
  "resourceType": "TutorNotificationTemplate",
  "template": "Hello user name: {{patient.name.given}}\n"
}

Let's create an instance of TutorNotificationTemplate resource with welcome message based on related patient's given name.

Now you can interact with created resources just like with any other FHIR resources.

Interact with a resource

GET /fhir/TutorNotification?_include=TutorNotification:subject:Patient

It allows you to make following requests:

POST /fhir/SearchParameter
content-type: application/json
accept: application/json

{
  "resourceType": "SearchParameter",
  "id": "TutorNotification-subject",
  "url": "http://example.com/aidbox-sms-tutor/TutorNotification-subject",
  "version": "0.0.1",
  "status": "draft",
  "name": "subject",
  "code": "subject",
  "base": [
    "TutorNotification"
  ],
  "type": "reference",
  "description": "Search TutorNotification by subject",
  "expression": "TutorNotification.subject"
}

The other one is used to include related Patient resources to the search bundle.

GET /fhir/TutorNotification?status=requested

This one defines the expression to achieve resource status, which allows you to search for TutorNotification resources by status like this:

POST /fhir/SearchParameter
content-type: application/json
accept: application/json

{
  "resourceType": "SearchParameter",
  "id": "TutorNotification-status",
  "url": "http://example.com/aidbox-sms-tutor/TutorNotification-status",
  "version": "0.0.1",
  "status": "draft",
  "name": "status",
  "code": "status",
  "base": [
    "TutorNotification"
  ],
  "type": "token",
  "description": "Search TutorNotification by status",
  "expression": "TutorNotification.status"
}

Let's create the search parameters mentioned above.

With defined resources, most of the work is done, but there is one missing aspect of any FHIR resource. You definitely want to check your requested notifications or include related subjects to the search bundle. Aidbox allows you to define SearchParameter resources in addition to custom resources.

Convenience

Manually writing StructureDefinitions can be overwhelming. Fortunately, there is an alternative: FSH/SUSHI allows you to generate a FHIR NPM package with your custom resources, which you can load into Aidbox and use.

Upload FHIR Implementation Guide

How Aidbox Deals with FHIR Limitations for Custom Resources

FHIR Type ValueSet Bindings

FHIR defines certain ValueSets that list all resource types and binds them with required strength to some properties. For example, the SearchParameter.base property points to the resource this SearchParameter is intended for and has this exact binding. Obviously, your custom resource type is not mentioned in this ValueSet. However, you still want to create and use search parameters for your custom resources.

During validation, Aidbox checks whether the resource type is in the given ValueSet or if it is a known custom resource for Aidbox. This allows you to use custom resources in resources like CapabilityStatement or SearchParameter, or in the type property of references.

References to Unknown FHIR Types

FHIR allows references to point only to FHIR resources. Aidbox, however, allows you to specify custom resources in reference targets as well.

Bundle Entries Must Inherit from Resource FHIR Type

FHIR explicitly states that Bundle.entry.resource must be a type that inherits from the Resource FHIR type. Aidbox relaxes this constraint and checks that the referenced resource inherits from at least one StructureDefinition/FHIRSchema with kind: resource and derivation: specialization.

Last updated