Skip to main content

Implementation Features

Spec: FHIR Versioning, Version-aware Updates
FeatureSupportNotes
Sequential Versioningβœ… FullInteger versionIds starting from 1
Instance Historyβœ… FullAll versions of a single resource
Type Historyβœ… FullAll versions of a resource type
System Historyβœ… FullAll versions across all resource types
Versioned Read (vread)βœ… FullRead specific version by versionId
Version-aware Updatesβœ… FullIf-Match header support
History Bundlesβœ… FullHistory replication via Bundle.type=history
Soft Delete Historyβœ… FullDeleted versions preserved in history
Hard Delete Historyβœ… ConfigurableComplete removal including history
History Parametersβœ… Full_count, _since, _sort support

Version Management

Sequential Integer Versions

TLQ FHIR uses sequential integer versions starting at 1 for each resource:
// First version (create)
{
  "resourceType": "Patient",
  "id": "123",
  "meta": {
    "versionId": "1",
    "lastUpdated": "2026-01-12T10:00:00Z"
  },
  "name": [{ "family": "Doe", "given": ["John"] }]
}

// Second version (update)
{
  "resourceType": "Patient",
  "id": "123",
  "meta": {
    "versionId": "2",
    "lastUpdated": "2026-01-12T11:00:00Z"
  },
  "name": [{ "family": "Doe", "given": ["Jane"] }]
}

Version Lifecycle

Each CRUD operation affects versioning differently:
OperationVersion BehaviorExample
CREATECreates version 1POST /Patient β†’ version 1
UPDATEIncrements versionPUT /Patient/123 β†’ version 2, 3, etc.
PATCHIncrements versionPATCH /Patient/123 β†’ version 2, 3, etc.
DELETE (soft)Creates deletion versionDELETE /Patient/123 β†’ version 3 (marked deleted)
DELETE (hard)Removes all versionsDELETE /Patient/123 β†’ all history gone

Database Storage

TLQ FHIR stores versions in two tables:
  • resources: All resource versions with metadata
  • resource_versions: Version counter per resource
-- Current version lookup (fast)
SELECT * FROM resources
WHERE resource_type = 'Patient' AND id = '123' AND is_current = true;

-- Specific version lookup
SELECT * FROM resources
WHERE resource_type = 'Patient' AND id = '123' AND version_id = 2;

Versioned Read (vread)

Spec: vread Read a specific version of a resource:
# Read version 2 of Patient/123
curl http://localhost:8080/fhir/Patient/123/_history/2

Response Behavior

Successful vread (200 OK):
{
  "resourceType": "Patient",
  "id": "123",
  "meta": {
    "versionId": "2",
    "lastUpdated": "2026-01-12T11:00:00Z"
  },
  "name": [{ "family": "Doe", "given": ["Jane"] }]
}
Version not found (404 Not Found):
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "not-found",
      "diagnostics": "Version 5 of Patient/123 not found"
    }
  ]
}
Deleted version (410 Gone):
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "deleted",
      "diagnostics": "Version 3 of Patient/123 was deleted"
    }
  ]
}

HEAD Support

Check version existence without fetching body:
curl -I http://localhost:8080/fhir/Patient/123/_history/2

# 200 OK = version exists
# 404 Not Found = version doesn't exist
# 410 Gone = version was a deletion

Caching Headers

vread responses include immutable caching headers since versions never change:
HTTP/1.1 200 OK
ETag: W/"2"
Last-Modified: Mon, 12 Jan 2026 11:00:00 GMT
Cache-Control: public, max-age=31536000, immutable

History Operations

Instance History

Spec: Instance History Get all versions of a specific resource:
curl http://localhost:8080/fhir/Patient/123/_history
Response (Bundle with type=β€œhistory”):
{
  "resourceType": "Bundle",
  "type": "history",
  "total": 3,
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "123",
        "meta": { "versionId": "2", "lastUpdated": "2026-01-12T11:00:00Z" },
        "name": [{ "family": "Doe", "given": ["Jane"] }]
      },
      "request": { "method": "PUT", "url": "Patient/123" }
    },
    {
      "resource": {
        "resourceType": "Patient",
        "id": "123",
        "meta": { "versionId": "1", "lastUpdated": "2026-01-12T10:00:00Z" },
        "name": [{ "family": "Doe", "given": ["John"] }]
      },
      "request": { "method": "POST", "url": "Patient" }
    }
  ]
}

Type History

Get history for all resources of a specific type:
curl http://localhost:8080/fhir/Patient/_history

System History

Get history across all resource types:
curl http://localhost:8080/fhir/_history

History Parameters

All history endpoints support standard parameters:
ParameterDescriptionExample
_countLimit results?_count=10
_sinceOnly versions after timestamp?_since=2026-01-12T10:00:00Z
_sortSort direction?_sort=_lastUpdated (asc), ?_sort=-_lastUpdated (desc)
# Get last 5 versions since yesterday
curl "http://localhost:8080/fhir/Patient/123/_history?_count=5&_since=2026-01-11T00:00:00Z"

History Entry Methods

TLQ FHIR automatically determines the HTTP method for each history entry:
VersionConditionMethodNotes
1First versionPOSTResource creation
2+Not deletedPUTResource update
AnyDeleted flagDELETEResource deletion

Version-Aware Operations

Optimistic Locking with If-Match

Spec: Version-aware Updates Always use If-Match for safe updates:
# 1. Read current version
curl -i http://localhost:8080/fhir/Patient/123
# ETag: W/"2"

# 2. Update with version check
curl -X PUT http://localhost:8080/fhir/Patient/123 \
  -H "Content-Type: application/fhir+json" \
  -H 'If-Match: W/"2"' \
  -d '{
    "resourceType": "Patient",
    "id": "123",
    "name": [{ "family": "Doe", "given": ["Jane"] }]
  }'

Version Conflict Handling

Successful update (200 OK):
{
  "resourceType": "Patient",
  "id": "123",
  "meta": {
    "versionId": "3",
    "lastUpdated": "2026-01-12T12:00:00Z"
  }
}
Version conflict (412 Precondition Failed):
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "conflict",
      "diagnostics": "Version conflict: expected 2, actual 3"
    }
  ]
}

Recovery from Conflicts

When version conflicts occur:
  1. Refetch the current version
  2. Merge your changes with current state
  3. Retry with new version
# Conflict occurred, refetch current state
curl -i http://localhost:8080/fhir/Patient/123
# ETag: W/"3"

# Retry with correct version
curl -X PUT http://localhost:8080/fhir/Patient/123 \
  -H 'If-Match: W/"3"' \
  -d '{ /* merged changes */ }'

Delete and History

Soft Delete (Default)

With soft delete (hard_delete: false):
curl -X DELETE http://localhost:8080/fhir/Patient/123
Behavior:
  • Creates new version marked as deleted
  • History remains accessible
  • GET /Patient/123 β†’ 410 Gone
  • GET /Patient/123/_history β†’ 200 OK (full history)
  • GET /Patient/123/_history/3 β†’ 410 Gone (deletion version)

Hard Delete

With hard delete (hard_delete: true):
curl -X DELETE http://localhost:8080/fhir/Patient/123
Behavior:
  • Physically removes all versions from database
  • History becomes inaccessible
  • GET /Patient/123 β†’ 404 Not Found
  • GET /Patient/123/_history β†’ 404 Not Found
  • Enables history deletion endpoints

History Deletion (Hard Delete Only)

When hard delete is enabled, you can delete specific versions or entire history:
# Delete specific version
curl -X DELETE http://localhost:8080/fhir/Patient/123/_history/2

# Delete entire history
curl -X DELETE http://localhost:8080/fhir/Patient/123/_history
Irreversible Operation: Hard delete permanently removes all data and history. This cannot be undone. Use with extreme caution in production.

History Bundles (Replication)

Spec: Bundle.type=history TLQ FHIR supports history bundle replication for data synchronization:
curl -X POST http://localhost:8080/fhir \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Bundle",
    "type": "history",
    "entry": [
      {
        "resource": {
          "resourceType": "Patient",
          "id": "123",
          "meta": { "versionId": "1" },
          "name": [{ "family": "Doe", "given": ["John"] }]
        }
      },
      {
        "resource": {
          "resourceType": "Patient",
          "id": "123",
          "meta": { "versionId": "2" },
          "name": [{ "family": "Doe", "given": ["Jane"] }]
        }
      }
    ]
  }'

History Bundle Processing

TLQ FHIR processes history bundles with these rules:
  1. Sequential Processing: Entries processed in order (not atomic)
  2. ID Preservation: Resource IDs preserved from bundle
  3. Version Checking: Older/duplicate versions ignored
  4. Create or Update: Creates if resource doesn’t exist, updates if newer
  5. Delete Handling: Entries with response.status="410 Gone" trigger deletion

Version Conflict Resolution

{
  "resourceType": "Bundle",
  "type": "batch-response",
  "entry": [
    {
      "response": {
        "status": "200 OK",
        "location": "Patient/123/_history/2"
      }
    },
    {
      "response": {
        "status": "200 OK",
        "outcome": {
          "resourceType": "OperationOutcome",
          "issue": [
            {
              "severity": "information",
              "code": "informational",
              "diagnostics": "Ignored older or duplicate history entry"
            }
          ]
        }
      }
    }
  ]
}

Performance Considerations

Indexing Strategy

TLQ FHIR indexes key version-related columns:
-- Primary lookup indexes
CREATE INDEX idx_resources_current ON resources (resource_type, id, is_current);
CREATE INDEX idx_resources_version ON resources (resource_type, id, version_id);
CREATE INDEX idx_resources_updated ON resources (last_updated DESC);

-- History query optimization
CREATE INDEX idx_resources_history ON resources (resource_type, id, last_updated DESC, version_id DESC);

History Query Optimization

For large history sets, use pagination:
# Get first page
curl "http://localhost:8080/fhir/Patient/_history?_count=50"

# Get next page using _cursor from previous response
curl "http://localhost:8080/fhir/Patient/_history?_count=50&_cursor=eyJ..."

Version Cleanup

Configure version limits to manage storage:
fhir:
  max_history_versions: 100 # Keep last 100 versions per resource
  history_cleanup_enabled: true
  history_cleanup_interval: "24h"
Version Cleanup: When enabled, TLQ FHIR automatically removes old versions beyond the configured limit, keeping the most recent versions.

Configuration

Key configuration options for versioning and history:
fhir:
  # Delete behavior
  hard_delete: false # Use soft delete (preserves history)

  # History limits
  max_history_versions: 0 # 0 = unlimited, >0 = keep N versions
  history_cleanup_enabled: false
  history_cleanup_interval: "24h"

  # History bundle processing
  allow_history_bundles: true

  # Performance
  history_page_size: 100 # Default page size for history queries

  # Interactions (can be disabled per type)
  interactions:
    instance:
      history: true # GET /Patient/123/_history
      vread: true # GET /Patient/123/_history/2
    type:
      history: true # GET /Patient/_history
    system:
      history: true # GET /_history
      history_bundle: true # POST / with Bundle.type=history

Error Handling

Common versioning-related errors:
HTTP StatusMeaningExample
404 Not FoundVersion doesn’t existWrong versionId
410 GoneVersion was deletedReading deleted version
412 Precondition FailedVersion conflictIf-Match mismatch

Error Response Examples

Version not found:
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "not-found",
      "diagnostics": "Version 5 of Patient/123 not found"
    }
  ]
}
Version conflict:
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "conflict",
      "diagnostics": "Version conflict: expected 2, actual 3. Resource was modified by another client."
    }
  ]
}

Testing Your Implementation

Use the interactive API playground to test versioning operations:

Next Steps