AnpherosAnpheros PlatformDevelopers

FHIR vs. a custom medical database

Your own tables or HL7 FHIR R4 for medical data: the same reading stored both ways, the trade-offs side by side, when each is right, and the usual split between your database and the FHIR record.

A team starting a health product usually faces the same choice early: design its own tables for patients, measurements and diagnoses in PostgreSQL or another database, or store medical data as HL7 FHIR R4 resources from the start. A custom schema is faster on day one and fits your product exactly. FHIR costs more thought up front and pays back every time the data has to leave your system, be audited, be shared with consent or be read by someone who is not you. This page sets out the trade-offs honestly, with the same reading stored both ways, and says when each choice is right.

This is a different question from which FHIR server to use. For that, see How Anpheros compares with other FHIR servers.

The same reading, two ways

A blood-pressure reading in a custom schema:

CREATE TABLE bp_readings (
  id          uuid PRIMARY KEY,
  user_id     uuid REFERENCES users(id),
  systolic    int,
  diastolic   int,
  measured_at timestamptz,
  created_at  timestamptz DEFAULT now()
);

Simple, fast to query, exactly what the app needs. Then come the questions the table does not answer:

The same reading as FHIR R4:

{"resourceType": "Observation", "status": "final",
 "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
 "code": {"coding": [{"system": "http://loinc.org", "code": "85354-9", "display": "Blood pressure panel"}]},
 "subject": {"reference": "Patient/d09e3942-…"}, "effectiveDateTime": "2026-10-10T08:30:00Z",
 "component": [
   {"code": {"coding": [{"system": "http://loinc.org", "code": "8480-6"}]}, "valueQuantity": {"value": 138, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}},
   {"code": {"coding": [{"system": "http://loinc.org", "code": "8462-4"}]}, "valueQuantity": {"value": 88, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}}],
 "meta": {"versionId": "1"}}

More verbose, but every part has a meaning any FHIR system shares: LOINC says what was measured, UCUM says the unit, meta.versionId starts a history, and the same Observation shape holds the other 200 measurements. With Anpheros, a Provenance resource also records who wrote it. How to store medical records using FHIR R4 walks through it.

Side by side

Custom schema FHIR R4
Time to the first feature hours a day or two to learn the resources
Fit to your product exact general; your product's view sits on top
Adding a new kind of data a migration per kind usually a new code in an existing resource
Meaning outside your system none without a mapping shared codes (LOINC, ICD-10, ATC, UCUM)
History and corrections you design it versions and _history by design
Who wrote what you design it Provenance (automatic in Anpheros)
Exchange with clinics, labs, other apps custom integration each time FHIR R4 API, HL7 v2 import, International Patient Summary
Patient consent per application you design it SMART on FHIR scopes and OAuth
Querying for analytics plain SQL FHIR search, or export to your warehouse
Regulation (GDPR Article 9, EHDS) you document every part standard formats the regulation refers to

When a custom schema is the right call

When FHIR is the right call

The usual middle path

Most products end up with both, split by kind of data:

your database                         FHIR record (Anpheros)
─────────────                         ──────────────────────
users, sessions, settings             Patient
subscriptions, billing                Observation, Condition, MedicationStatement
product content, feature flags        AllergyIntolerance, Immunization, DocumentReference
user id → patient id (one column)     Provenance, Consent, history
caches and analytics copies  ◄─────── webhooks

Your database stays small and product-shaped. The medical record is standard, versioned and shareable, and you did not have to build it. Medical app backend and database describes this split in detail, and How to build a healthcare API with TypeScript builds it.

What it takes to build FHIR yourself

Choosing FHIR does not mean writing a FHIR server. Your options are an open-source server you run, a cloud provider's FHIR store, an open-source healthcare platform, or a managed healthcare database like Anpheros. They differ in who handles consent, audit, provenance, residency and integrations, which How Anpheros compares with other FHIR servers sets out.

Frequently asked questions

Is FHIR a database?

No. FHIR is a standard for the structure and exchange of health data: resources, codes and a REST API. A FHIR server or managed FHIR database stores the resources and implements the API on top of a real database.

Can I store FHIR resources in PostgreSQL myself?

Yes, as JSON columns with your own indexes, and some open-source servers do exactly that. The work is in everything around storage: search parameters, versioning, references, validation, access control per patient and provenance.

Can I start with a custom schema and move to FHIR later?

You can, and many teams do, but the migration is a mapping project: every table needs codes, units and authors it never recorded. Storing medical data as FHIR from the first feature costs less than converting it later.

Do I need FHIR to comply with GDPR?

No. GDPR does not require a particular format. FHIR makes several obligations easier to meet: portability (the record exports in a standard format), access control per purpose, and showing who processed which data.

Related