> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ferrum.thalamiq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Referential Integrity

> How Ferrum validates references between FHIR resources on create, update, and delete.

## 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.

Ferrum supports two modes:

| Mode        | Behavior                                                                       |
| ----------- | ------------------------------------------------------------------------------ |
| **lenient** | No reference checking. Dangling references are allowed. (Default)              |
| **strict**  | Rejects writes with broken references. Blocks deletes of referenced resources. |

## Configuration

### Config file

```yaml theme={null}
fhir:
  referential_integrity:
    mode: lenient # "lenient" (default) or "strict"
```

### Environment variable

```bash theme={null}
FHIR__REFERENTIAL_INTEGRITY__MODE=strict
```

## Behavior

### On create and update (strict mode)

When a resource is created or updated, Ferrum 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:

```json theme={null}
{
  "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, Ferrum checks whether any other resource references it via the `search_reference` index. If references exist, the delete is blocked with `409 Conflict`:

```json theme={null}
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "business-rule",
      "diagnostics": "Cannot delete Patient/123: it is referenced by Observation/abc, Condition/def"
    }
  ]
}
```

<Warning>
  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.
</Warning>

### 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

<CardGroup cols={2}>
  <Card title="CRUD Operations" href="/server/crud-operations">
    Learn about create, read, update, delete
  </Card>

  <Card title="Batch & Transaction" href="/server/batch-and-transactions">
    Perform multiple operations atomically
  </Card>

  <Card title="Configuration" href="/getting-started/configuration">
    Configure server behavior
  </Card>

  <Card title="Search" href="/server/search">
    Query resources efficiently
  </Card>
</CardGroup>
