Skip to main content

Storage model

Everything the server stores lives in a small, fixed set of PostgreSQL tables — around a dozen, no matter how many resource types you use. Adding a new resource type, profile, or search parameter never requires a schema migration.

Every write is atomic: the resource, its history snapshot, and its search-index entries all commit in one transaction, so a resource is never searchable in a state that was not stored. Creates, updates, and deletes each append an immutable snapshot, which is what powers versioned reads, If-Match optimistic locking, and audit trails. Deletes are soft, so history remains readable after one.

The tables you will see​

Operators looking at the database will find:

TablePurpose
resourcesCurrent JSON representation and metadata for every resource
resource_historyImmutable version snapshots
sp_string, sp_token, sp_date, sp_number, sp_quantity, sp_uri, sp_reference, sp_coordsTyped search values extracted at write time
search_param_definitionsBase, IG-provided, and custom SearchParameter definitions
ig_packages, ig_profiles, base_definitionsLoaded FHIR packages and StructureDefinitions
schema_versionApplied schema revision

Search never scans raw JSON: queries resolve against the typed sp_* indexes first and load the matching documents last, which keeps search latency predictable as data grows.

Schema management​

The canonical schema is internal/db/schema.sql. It is idempotent; production deployments should apply schema changes out of band with a controlled database role rather than granting the runtime role DDL privileges.