Implementation Features
π Spec: FHIR Versioning,
Version-aware Updates
| Feature | Support | Notes |
|---|
| Sequential Versioning | β
Full | Integer versionIds starting from 1 |
| Instance History | β
Full | All versions of a single resource |
| Type History | β
Full | All versions of a resource type |
| System History | β
Full | All versions across all resource types |
| Versioned Read (vread) | β
Full | Read specific version by versionId |
| Version-aware Updates | β
Full | If-Match header support |
| History Bundles | β
Full | History replication via Bundle.type=history |
| Soft Delete History | β
Full | Deleted versions preserved in history |
| Hard Delete History | β
Configurable | Complete 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:
| Operation | Version Behavior | Example |
|---|
| CREATE | Creates version 1 | POST /Patient β version 1 |
| UPDATE | Increments version | PUT /Patient/123 β version 2, 3, etc. |
| PATCH | Increments version | PATCH /Patient/123 β version 2, 3, etc. |
| DELETE (soft) | Creates deletion version | DELETE /Patient/123 β version 3 (marked deleted) |
| DELETE (hard) | Removes all versions | DELETE /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
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:
| Parameter | Description | Example |
|---|
_count | Limit results | ?_count=10 |
_since | Only versions after timestamp | ?_since=2026-01-12T10:00:00Z |
_sort | Sort 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:
| Version | Condition | Method | Notes |
|---|
| 1 | First version | POST | Resource creation |
| 2+ | Not deleted | PUT | Resource update |
| Any | Deleted flag | DELETE | Resource 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:
- Refetch the current version
- Merge your changes with current state
- 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:
- Sequential Processing: Entries processed in order (not atomic)
- ID Preservation: Resource IDs preserved from bundle
- Version Checking: Older/duplicate versions ignored
- Create or Update: Creates if resource doesnβt exist, updates if newer
- 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"
}
]
}
}
}
]
}
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 Status | Meaning | Example |
|---|
404 Not Found | Version doesnβt exist | Wrong versionId |
410 Gone | Version was deleted | Reading deleted version |
412 Precondition Failed | Version conflict | If-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