Skip to main content

Overview

FHIR resources frequently reference each other. For example, an Observation may reference a Patient via Observation.subject. Referential integrity controls whether the server validates that these references point to resources that actually exist. Zunder supports two modes:
ModeBehavior
lenientNo reference checking. Dangling references are allowed. (Default)
strictRejects writes with broken references. Blocks deletes of referenced resources.

Configuration

Config file

fhir:
  referential_integrity:
    mode: lenient # "lenient" (default) or "strict"

Environment variable

FHIR__REFERENTIAL_INTEGRITY__MODE=strict

Behavior

On create and update (strict mode)

When a resource is created or updated, Zunder extracts all references from the resource JSON and validates that each referenced resource exists in the database. What is checked:
  • Relative references (e.g., Patient/123, Observation/abc)
What is NOT checked:
  • Fragment references to contained resources (e.g., #p1)
  • Absolute URLs to external servers (e.g., http://external.example.com/Patient/1)
  • Canonical URLs (e.g., http://hl7.org/fhir/StructureDefinition/Patient)
  • Self-references (a resource referencing its own resourceType/id)
If any referenced resource does not exist, the server returns 409 Conflict with an OperationOutcome listing the broken references:
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "business-rule",
      "diagnostics": "Referential integrity violation: the following referenced resources do not exist: Patient/nonexistent-999"
    }
  ]
}

On delete (strict mode)

Before deleting a resource, Zunder checks whether any other resource references it via the search_reference index. If references exist, the delete is blocked with 409 Conflict:
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "business-rule",
      "diagnostics": "Cannot delete Patient/123: it is referenced by Observation/abc, Condition/def"
    }
  ]
}
The delete check queries the search_reference index table, which is populated by the indexing pipeline. If indexing has not yet completed for a recently created resource, the reverse reference may not be detected.

In batch bundles

Each entry in a batch bundle is validated independently. If one entry has broken references in strict mode, only that entry fails; other entries proceed normally.

In transaction bundles

Transaction bundles are validated with awareness of resources created earlier in the same transaction. For example, if a transaction creates a Patient in one entry and an Observation referencing that Patient in a subsequent entry, the reference is considered valid even though the Patient does not yet exist in the database. Delete entries within a transaction are also validated: you cannot delete a resource that is referenced by resources outside the transaction.

Lenient mode

In lenient mode (the default), no reference validation occurs. Resources can reference non-existent targets, and any resource can be deleted regardless of incoming references. This is the most permissive setting and matches the behavior of many FHIR servers.

Limitations

  • Only relative references are validated. External URLs, canonical references, and contained references are not checked.
  • No cascade delete. In strict mode, you must delete referencing resources before deleting the target. Cascade delete may be added in a future release.
  • Delete checks depend on the search index. If indexing is delayed (e.g., background job queue), recently created references may not block a delete.

Next Steps