TL;DR
HL7 FHIR (Fast Healthcare Interoperability Resources) ist der Standard für den Austausch von Gesundheitsdaten via RESTful APIs (JSON/XML). Es definiert normierte Ressourcen (Patient, Observation, Medication, etc.), nutzt OAuth 2.0-Authentifizierung und SMART on FHIR für App-Integration. Dieser Leitfaden zeigt Architektur, Ressourcentypen, Suchparameter, Authentifizierung und produktionsreife Implementierung – inklusive Codebeispiele.
💡 Apidog vereinfacht die Integration von Gesundheits-APIs. Teste FHIR-Endpunkte, validiere Ressourcenschemata, debugge Authentifizierungsabläufe und dokumentiere API-Spezifikationen zentral. Importiere FHIR Implementation Guides, mocke Antworten und teile Testszenarien mit deinem Team.
Was ist HL7 FHIR?
FHIR ist ein Standard-Framework für den elektronischen Austausch von Gesundheitsinformationen. Entwickelt von HL7, basiert FHIR auf RESTful APIs, JSON, XML und OAuth 2.0.
FHIR-Ressourcentypen
FHIR bietet über 140 Ressourcentypen. Die wichtigsten sind:
| Ressource | Zweck | Häufige Anwendungsfälle |
|---|---|---|
| Patient | Demografische Daten | Patientensuche, Registrierung |
| Practitioner | Anbieterinformationen | Verzeichnis, Terminplanung |
| Encounter | Besuche/Aufnahmen | Behandlungsepisoden, Abrechnung |
| Observation | Klinische Daten | Vitalwerte, Laborergebnisse, Beurteilungen |
| Condition | Probleme/Diagnosen | Problemlisten, Behandlungsplanung |
| MedicationRequest | Verschreibungen | E-Rezept, Medikationshistorie |
| AllergyIntolerance | Allergien | Sicherheitsprüfungen, Warnungen |
| Immunization | Impfungen | Impfprotokolle |
| DiagnosticReport | Labor-/Bildgebungsberichte | Ergebnisübermittlung |
| DocumentReference | Klinische Dokumente | CCD, Entlassungszusammenfassungen |
FHIR API-Architektur
FHIR folgt einem RESTful-API-Schema:
https://fhir-server.com/fhir/{resourceType}/{id}
FHIR-Versionen im Vergleich
| Version | Status | Anwendungsfall |
|---|---|---|
| R4 (4.0.1) | Aktueller STU | Produktionsimplementierungen |
| R4B (4.3) | Testimplementierung | Frühe Anwender |
| R5 (5.0.0) | Entwurf STU | Zukünftige Implementierungen |
| DSTU2 | Veraltet | Nur für Legacy-Systeme |
Tipp: CMS fordert FHIR R4-Unterstützung für zertifizierte EHRs.
Erste Schritte: FHIR-Server-Zugang
Schritt 1: Einen FHIR-Server wählen
| Server | Typ | Kosten | Beste Nutzung |
|---|---|---|---|
| Azure API for FHIR | Verwaltet | Pay-per-Use | Unternehmen, Azure-Nutzer |
| AWS HealthLake | Verwaltet | Pay-per-Use | AWS-Umgebungen |
| Google Cloud Healthcare API | Verwaltet | Pay-per-Use | GCP-Umgebungen |
| HAPI FHIR | Open Source | Selbst gehostet | Benutzerdefinierte Deployments |
| Epic FHIR Server | Kommerziell | Epic-Kunden | Epic EHR-Integration |
| Cerner Ignite FHIR | Kommerziell | Cerner-Kunden | Cerner EHR-Integration |
Schritt 2: Server-Anmeldeinformationen erhalten
Für Cloud-Dienste:
# Azure API for FHIR
# 1. FHIR-Dienst im Azure-Portal anlegen
# 2. OAuth 2.0/AAD-Authentifizierung konfigurieren
# 3. FHIR-Endpunkt notieren: https://{service-name}.azurehealthcareapis.com
# 4. Client-App für OAuth registrieren
# AWS HealthLake
# 1. Datenspeicher in AWS Console erstellen
# 2. IAM-Rollen zuweisen
# 3. Endpunkt übernehmen: https://healthlake.{region}.amazonaws.com
Schritt 3: FHIR RESTful-Operationen
| Operation | HTTP | Endpunkt | Beschreibung |
|---|---|---|---|
| Lesen | GET | /{resourceType}/{id} |
Ressource abrufen |
| Suchen | GET | /{resourceType}?param=value |
Ressourcen suchen |
| Erstellen | POST | /{resourceType} |
Neue Ressource erstellen |
| Aktualisieren | PUT | /{resourceType}/{id} |
Ressource ersetzen |
| Patchen | PATCH | /{resourceType}/{id} |
Partial Update |
| Löschen | DELETE | /{resourceType}/{id} |
Ressource entfernen |
| Historie | GET | /{resourceType}/{id}/_history |
Versionen anzeigen |
Schritt 4: FHIR-Server testen
curl -X GET "https://fhir-server.com/fhir/metadata" \
-H "Accept: application/fhir+json" \
-H "Authorization: Bearer {token}"
Erwartete Antwort:
{
"resourceType": "CapabilityStatement",
"status": "active",
"date": "2026-03-25",
"fhirVersion": "4.0.1",
"rest": [{
"mode": "server",
"resource": [
{ "type": "Patient" },
{ "type": "Observation" },
{ "type": "Condition" }
]
}]
}
FHIR-Kernoperationen
Patientenressource lesen
const FHIR_BASE_URL = process.env.FHIR_BASE_URL;
const FHIR_TOKEN = process.env.FHIR_TOKEN;
const fhirRequest = async (endpoint, options = {}) => {
const response = await fetch(`${FHIR_BASE_URL}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${FHIR_TOKEN}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR-Fehler: ${error.issue?.[0]?.details?.text || response.statusText}`);
}
return response.json();
};
// Patient nach ID lesen
const getPatient = async (patientId) => {
const patient = await fhirRequest(`/Patient/${patientId}`);
return patient;
};
// Beispiel
const patient = await getPatient('12345');
console.log(`Patient: ${patient.name[0].given[0]} ${patient.name[0].family}`);
console.log(`Geburtsdatum: ${patient.birthDate}`);
console.log(`Geschlecht: ${patient.gender}`);
Struktur einer Patientenressource
{
"resourceType": "Patient",
"id": "12345",
"identifier": [
{
"use": "usual",
"type": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR"
}]
},
"system": "http://hospital.example.org",
"value": "MRN123456"
}
],
"name": [
{
"use": "official",
"family": "Smith",
"given": ["John", "Michael"]
}
],
"telecom": [
{
"system": "phone",
"value": "555-123-4567",
"use": "home"
},
{
"system": "email",
"value": "john.smith@email.com"
}
],
"gender": "male",
"birthDate": "1985-06-15",
"address": [
{
"use": "home",
"line": ["123 Main Street"],
"city": "Springfield",
"state": "IL",
"postalCode": "62701"
}
]
}
Ressourcen suchen
const searchPatients = async (searchParams) => {
const query = new URLSearchParams();
if (searchParams.name) query.append('name', searchParams.name);
if (searchParams.birthDate) query.append('birthdate', searchParams.birthDate);
if (searchParams.identifier) query.append('identifier', searchParams.identifier);
if (searchParams.gender) query.append('gender', searchParams.gender);
const response = await fhirRequest(`/Patient?${query.toString()}`);
return response;
};
// Beispiel
const results = await searchPatients({ name: 'Smith', birthDate: '1985-06-15' });
console.log(`Gefunden ${results.total} Patienten`);
results.entry.forEach(entry => {
const patient = entry.resource;
console.log(`${patient.name[0].family}, ${patient.name[0].given[0]}`);
});
Häufige Suchparameter
| Ressource | Suchparameter | Beispiel |
|---|---|---|
| Patient | name, birthdate, identifier, gender, phone, email | ?name=Smith&birthdate=1985-06-15 |
| Observation | patient, code, date, category, status | ?patient=123&code=8480-6&date=ge2026-01-01 |
| Condition | patient, clinical-status, category, onset-date | ?patient=123&clinical-status=active |
| MedicationRequest | patient, status, intent, medication | ?patient=123&status=active |
| Encounter | patient, date, status, class | ?patient=123&date=ge2026-01-01 |
| DiagnosticReport | patient, category, date, status | ?patient=123&category=laboratory |
Suchmodifikatoren und Präfixe
| Modifikator | Beschreibung | Beispiel |
|---|---|---|
:exact |
Exakte Übereinstimmung | name:exact=Smith |
:contains |
Enthält | name:contains=smi |
:missing |
Hat/Fehlt | phone:missing=true |
Präfixe (ge, le, ...) |
Vergleich | birthdate=ge1980-01-01 |
| Präfix | Bedeutung | Beispiel |
|---|---|---|
eq |
Gleich | birthdate=eq1985-06-15 |
ne |
Ungleich | birthdate=ne1985-06-15 |
gt |
Größer als | birthdate=gt1980-01-01 |
lt |
Kleiner als | birthdate=lt1990-01-01 |
ge |
Größer oder gleich | birthdate=ge1980-01-01 |
le |
Kleiner oder gleich | birthdate=le1990-01-01 |
sa |
Später als | date=sa2026-01-01 |
eb |
Früher als | date=eb2026-12-31 |
Arbeiten mit klinischen Daten
Beobachtung (Vitalwert) erstellen
const createObservation = async (observationData) => {
const observation = {
resourceType: 'Observation',
status: 'final',
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/observation-category',
code: 'vital-signs'
}]
}
],
code: {
coding: [{
system: 'http://loinc.org',
code: observationData.code,
display: observationData.display
}]
},
subject: {
reference: `Patient/${observationData.patientId}`
},
effectiveDateTime: observationData.effectiveDate || new Date().toISOString(),
valueQuantity: {
value: observationData.value,
unit: observationData.unit,
system: 'http://unitsofmeasure.org',
code: observationData.ucumCode
},
performer: [
{
reference: `Practitioner/${observationData.performerId}`
}
]
};
const response = await fhirRequest('/Observation', {
method: 'POST',
body: JSON.stringify(observation)
});
return response;
};
// Beispiel: Blutdruck erfassen
const systolicBP = await createObservation({
patientId: '12345',
code: '8480-6',
display: 'Systolischer Blutdruck',
value: 120,
unit: 'mmHg',
ucumCode: 'mm[Hg]',
performerId: '67890'
});
console.log(`Beobachtung erstellt: ${systolicBP.id}`);
Häufige LOINC-Codes
| Code | Anzeige | Kategorie |
|---|---|---|
| 8480-6 | Systolischer Blutdruck | Vitalwerte |
| 8462-4 | Diastolischer Blutdruck | Vitalwerte |
| 8867-4 | Herzfrequenz | Vitalwerte |
| 8310-5 | Körpertemperatur | Vitalwerte |
| 8302-2 | Körpergröße | Vitalwerte |
| 29463-7 | Körpergewicht | Vitalwerte |
| 8871-5 | Atemfrequenz | Vitalwerte |
| 2339-0 | Glukose [Masse/Volumen] | Labor |
| 4548-4 | Hämoglobin A1c | Labor |
| 2093-3 | Cholesterin [Masse/Volumen] | Labor |
Condition (Diagnose) erstellen
const createCondition = async (conditionData) => {
const condition = {
resourceType: 'Condition',
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: conditionData.status || 'active'
}]
},
verificationStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
code: 'confirmed'
}]
},
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-category',
code: conditionData.category || 'problem-list-item'
}]
}
],
code: {
coding: [{
system: 'http://snomed.info/sct',
code: conditionData.sctCode,
display: conditionData.display
}]
},
subject: {
reference: `Patient/${conditionData.patientId}`
},
onsetDateTime: conditionData.onsetDate,
recordedDate: new Date().toISOString()
};
const response = await fhirRequest('/Condition', {
method: 'POST',
body: JSON.stringify(condition)
});
return response;
};
// Beispiel: Diabetes hinzufügen
const diabetes = await createCondition({
patientId: '12345',
sctCode: '44054006',
display: 'Diabetes mellitus Typ 2',
status: 'active',
category: 'problem-list-item',
onsetDate: '2024-01-15'
});
Häufige SNOMED CT-Codes
| Code | Anzeige | Kategorie |
|---|---|---|
| 44054006 | Diabetes mellitus Typ 2 | Problem |
| 38341003 | Hypertonie | Problem |
| 195967001 | Asthma | Problem |
| 13645005 | COPD | Problem |
| 35489007 | Depressive Störung | Problem |
| 22298006 | Myokardinfarkt | Problem |
| 26929004 | Alzheimer-Krankheit | Problem |
| 396275006 | Arthrose | Problem |
Patient-Medikationen abrufen
const getPatientMedications = async (patientId) => {
const response = await fhirRequest(
`/MedicationRequest?patient=${patientId}&status=active`
);
return response;
};
// Beispiel
const medications = await getPatientMedications('12345');
medications.entry?.forEach(entry => {
const med = entry.resource;
console.log(`${med.medicationCodeableConcept.coding[0].display}`);
console.log(` Dosis: ${med.dosageInstruction[0]?.text}`);
console.log(` Status: ${med.status}`);
});
Laborergebnisse abrufen
const getPatientLabResults = async (patientId, options = {}) => {
const params = new URLSearchParams({
patient: patientId,
category: options.category || 'laboratory'
});
if (options.dateFrom) params.append('date', `ge${options.dateFrom}`);
const response = await fhirRequest(`/DiagnosticReport?${params.toString()}`);
return response;
};
// Spezifischen Labortest (z.B. HbA1c) holen
const getLabValue = async (patientId, loincCode) => {
const params = new URLSearchParams({
patient: patientId,
code: loincCode
});
const response = await fhirRequest(`/Observation?${params.toString()}`);
return response;
};
// Beispiel: HbA1c abrufen
const hba1c = await getLabValue('12345', '4548-4');
hba1c.entry?.forEach(entry => {
const obs = entry.resource;
console.log(`HbA1c: ${obs.valueQuantity.value} ${obs.valueQuantity.unit}`);
console.log(`Datum: ${obs.effectiveDateTime}`);
});
OAuth 2.0 und SMART on FHIR
FHIR-Authentifizierung
FHIR-Server nutzen OAuth 2.0 & OpenID Connect:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │───▶│ Auth │───▶│ FHIR │
│ (App) │ │ Server │ │ Server │
└─────────────┘ └─────────────┘ └─────────────┘
SMART on FHIR App-Start (PKCE)
const crypto = require('crypto');
class SMARTClient {
constructor(config) {
this.clientId = config.clientId;
this.redirectUri = config.redirectUri;
this.issuer = config.issuer;
this.scopes = config.scopes;
}
generatePKCE() {
const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
return { codeVerifier, codeChallenge };
}
buildAuthUrl(state, patientId = null) {
const { codeVerifier, codeChallenge } = this.generatePKCE();
this.codeVerifier = codeVerifier;
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: this.scopes.join(' '),
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
aud: this.issuer,
launch: patientId ? `patient-${patientId}` : null
});
return `${this.issuer}/authorize?${params.toString()}`;
}
async exchangeCodeForToken(code) {
const response = await fetch(`${this.issuer}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: this.redirectUri,
client_id: this.clientId,
code_verifier: this.codeVerifier
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
patientId: data.patient,
encounterId: data.encounter
};
}
}
// Beispiel
const smartClient = new SMARTClient({
clientId: 'my-app-client-id',
redirectUri: 'https://myapp.com/callback',
issuer: 'https://fhir.epic.com',
scopes: [
'openid',
'profile',
'patient/Patient.read',
'patient/Observation.read',
'patient/Condition.read',
'patient/MedicationRequest.read',
'offline_access'
]
});
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`Weiterleiten zu: ${authUrl}`);
Relevante SMART-Scopes
| Scope | Berechtigung | Anwendungsfall |
|---|---|---|
openid |
OIDC-Authentifizierung | Alle Apps |
profile |
Benutzerprofil | Anbieterverzeichnis |
patient/Patient.read |
Patient lesen | Patientensuche |
patient/Observation.read |
Beobachtungen lesen | Vitalwerte, Labore |
patient/Condition.read |
Bedingungen lesen | Problemlisten |
patient/MedicationRequest.read |
Medikationen lesen | Medikationshistorie |
patient/*.read |
Alle Patientenressourcen lesen | Alle Patientendaten |
user/*.read |
Alle Ressourcen lesen | Anbieteransicht |
offline_access |
Refresh Token | Persistente Sitzungen |
Authentifizierte FHIR-Anfragen
class FHIRClient {
constructor(accessToken, fhirBaseUrl) {
this.accessToken = accessToken;
this.baseUrl = fhirBaseUrl;
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${this.accessToken}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR-Fehler: ${error.issue?.[0]?.details?.text}`);
}
return response.json();
}
async getPatient(patientId) {
return this.request(`/Patient/${patientId}`);
}
async searchPatients(params) {
const query = new URLSearchParams(params);
return this.request(`/Patient?${query.toString()}`);
}
}
// Beispiel nach OAuth-Callback
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);
Batch- und Transaktionsoperationen
Batch-Anfragen
const batchRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'batch',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
// Beispiel: Mehrere Ressourcen mit einer Anfrage holen
const bundle = await batchRequest([
{ method: 'GET', url: 'Patient/12345' },
{ method: 'GET', url: 'Patient/12345/Observation?category=vital-signs' },
{ method: 'GET', url: 'Patient/12345/Condition?clinical-status=active' }
]);
bundle.entry.forEach((entry, index) => {
console.log(`Antwort ${index}: ${entry.response.status}`);
console.log(entry.resource);
});
Transaktionsanfragen
const transactionRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'transaction',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
// Beispiel: Patient und Diagnose atomar anlegen
const transaction = await transactionRequest([
{
method: 'POST',
url: 'Patient',
resource: {
resourceType: 'Patient',
name: [{ family: 'Doe', given: ['Jane'] }],
gender: 'female',
birthDate: '1990-01-01'
}
},
{
method: 'POST',
url: 'Condition',
resource: {
resourceType: 'Condition',
clinicalStatus: { coding: [{ code: 'active' }] },
code: { coding: [{ system: 'http://snomed.info/sct', code: '38341003' }] },
subject: { reference: 'Patient/-1' }
}
}
]);
Abonnements und Webhooks
FHIR-Abonnements (R4B+)
const createSubscription = async (subscriptionData) => {
const subscription = {
resourceType: 'Subscription',
status: 'requested',
criteria: subscriptionData.criteria,
reason: subscriptionData.reason,
channel: {
type: 'rest-hook',
endpoint: subscriptionData.endpoint,
payload: 'application/fhir+json'
}
};
const response = await fhirRequest('/Subscription', {
method: 'POST',
body: JSON.stringify(subscription)
});
return response;
};
// Beispiel: Laborergebnisse abonnieren
const subscription = await createSubscription({
criteria: 'DiagnosticReport?category=laboratory&patient=12345',
reason: 'Laborergebnisse des Patienten überwachen',
endpoint: 'https://myapp.com/webhooks/fhir'
});
Webhook-Handler
const express = require('express');
const app = express();
app.post('/webhooks/fhir', express.json({ type: 'application/fhir+json' }), async (req, res) => {
const notification = req.body;
// Abonnementreferenz prüfen
if (notification.subscription !== expectedSubscription) {
return res.status(401).send('Nicht autorisiert');
}
// Benachrichtigung verarbeiten
if (notification.event?.resourceType === 'DiagnosticReport') {
const reportId = notification.event.resourceId;
const report = await fhirRequest(`/DiagnosticReport/${reportId}`);
await processLabResult(report);
}
res.status(200).send('OK');
});
Fehlerbehandlung
401 Nicht autorisiert
Symptome: "Nicht autorisiert"/"Ungültiges Token"
Lösungen:
- Token-Gültigkeit prüfen
- Scope korrekt?
-
Authorization: Bearer {token}Header gesetzt? - Stimmt Token-Audience zur FHIR-Server-URL?
403 Verboten
Symptome: Token gültig, aber Zugriff verweigert
Lösungen:
- Benutzerberechtigung prüfen
- Patientenkontext korrekt?
- SMART-Scopes passend?
- Zugriffskontrolle auf Ressourcen prüfen
404 Nicht gefunden
Symptome: Ressource nicht vorhanden, Endpunkt falsch
Lösungen:
- Ressourcen-ID korrekt?
- FHIR-Basis-URL korrekt?
- Ressourcentyp supported?
- Version korrekt angegeben?
422 Unverarbeitbare Entität
Symptome: Validierungsfehler beim Erstellen/Aktualisieren
Lösungen (Fehler auslesen):
const error = await response.json();
error.issue?.forEach(issue => {
console.log(`Schweregrad: ${issue.severity}`);
console.log(`Ort: ${issue.expression?.join('.')}`);
console.log(`Nachricht: ${issue.details?.text}`);
});
Häufige Ursachen: Pflichtfeld fehlt, ungültige Codes, Referenz- oder Datumsformatfehler
Produktions-Checkliste
Vor Livegang:
- [ ] OAuth 2.0 mit SMART on FHIR implementieren
- [ ] Token-Refresh-Logik einbauen
- [ ] Fehlerbehandlung aufbauen
- [ ] Logging (keine PHI!) sicherstellen
- [ ] Ratenbegrenzung hinzufügen
- [ ] Retry-Logik (exponentielles Backoff)
- [ ] EHR-Anbieter-übergreifende Tests
- [ ] Ressourcen mit FHIR-Validator prüfen
- [ ] API-Operationen dokumentieren
- [ ] Überwachung/Monitoring aktivieren
- [ ] Runbook für Incident-Response erstellen
FHIR-Validierung
const { FhirValidator } = require('fhir-validator');
const validator = new FhirValidator('4.0.1');
const validateResource = async (resource) => {
const validationResult = await validator.validate(resource);
if (!validationResult.valid) {
validationResult.issues.forEach(issue => {
console.error(`Validierungsfehler: ${issue.message}`);
console.error(`Ort: ${issue.path}`);
});
throw new Error('Ressourcenvalidierung fehlgeschlagen');
}
return true;
};
// Vor POST/PUT anwenden
await validateResource(patientResource);
Praxisanwendungen
Patientenportal
- Problem: Patienten konnten nicht auf Akten mehrerer Anbieter zugreifen
- Lösung: SMART on FHIR-App mit Epic- und Cerner-Anbindung
- Ergebnis: 80% Akzeptanz, 50% weniger Aktenanfragen
Umsetzen:
- Patientenorientierter SMART-App-Start
- Zugriff auf Patient, Observation, Condition, MedicationRequest
- Refresh Token nutzen
- Mobile UI
Klinische Entscheidungsunterstützung
- Problem: Präventionsbedarf nicht erkannt
- Lösung: Echtzeit-FHIR-Abfragen für Versorgungslücken
- Ergebnis: 25% bessere HEDIS-Scores
Umsetzen:
- Anbieterorientierte SMART-App
- FHIR-Abfragen (Patient, Condition, Observation, Immunization)
- Versorgungslücken-Logik
- Empfehlungen im EHR
Analysen zur Bevölkerungsgesundheit
- Problem: Unvollständige Daten über Anbieter hinweg
- Lösung: FHIR-Bulk-Export
- Ergebnis: 360°-Patientenansicht, niedrigere PMPM-Kosten
Umsetzen:
- Bulk Data Access ($export)
- Nächtliche Exporte ins DWH
- Risikomodelle
- Care-Manager-Benachrichtigungen
Fazit
- FHIR R4 ist der aktuelle API-Standard im Gesundheitswesen
- SMART on FHIR = sichere Authentifizierung (OAuth 2.0)
- Ressourcentypen standardisieren Patient, Beobachtung, Medikation
- Flexible, gezielte Suchabfragen möglich
- Batch-/Transaktionsoperationen für komplexe Workflows
- Apidog optimiert FHIR-API-Tests und Dokumentation
FAQ-Bereich
Wofür wird HL7 FHIR verwendet?
FHIR standardisiert den Datenaustausch zwischen EHRs, Portalen, mobilen Apps & Systemen. Anwendungsfälle: Patientenportale, CDS, Population Health, Koordination.
Wie fange ich mit FHIR an?
Nutze öffentliche FHIR-Server (z.B. HAPI FHIR-Testserver) oder richte einen Cloud-Dienst (Azure, AWS) ein. Übe das Lesen/Suchen von Ressourcen.
Was ist der Unterschied zwischen HL7 v2 und FHIR?
HL7 v2 = Pipe-getrennte Nachrichten (ADT, ORM, ORU). FHIR = RESTful APIs mit JSON/XML, ressourcenbasiert, moderner, webfähig.
Ist FHIR HIPAA-konform?
FHIR selbst ist nur Standard. HIPAA-Konformität ergibt sich aus Implementierung: Verschlüsselung, Auth, Zugriffskontrolle, Logging. Nutze OAuth 2.0 + SMART on FHIR.
Was sind SMART-Scopes?
SMART-Scopes regeln granular den Ressourcenzugriff (z.B. patient/Observation.read). Fordere nur nötige Scopes an.
Wie suche ich nach Ressourcen in FHIR?
GET mit Parametern wie /Patient?name=Smith&birthdate=ge1980-01-01. Modifikatoren wie :exact, Präfixe wie ge, lt sind verfügbar.
Was ist Bulk FHIR?
Bulk FHIR ($export) erlaubt asynchronen Export großer NDJSON-Datensätze – für Population Health, Analytics, Data Warehouses.
Wie gehe ich mit FHIR-Versionierung um?
Definiere die Zielversion (empfohlen: R4), prüfe CapabilityStatement des Servers.
Kann ich FHIR mit benutzerdefinierten Feldern erweitern?
Ja, via Extensions – in Implementation Guides beschreiben und ggf. bei HL7 registrieren.
Welche Tools helfen bei der FHIR-Entwicklung?
z.B. HAPI FHIR Server, FHIR-Validator, Postman-Sammlungen, Apidog für API-Tests/Dokumentation.

Top comments (0)