DEV Community

Cover image for HL7 FHIR API Nutzung: Umfassende Anleitung zur Gesundheitswesen Integration (2026)
Emre Demir
Emre Demir

Posted on • Originally published at apidog.com

HL7 FHIR API Nutzung: Umfassende Anleitung zur Gesundheitswesen Integration (2026)

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.

Teste Apidog noch heute

💡 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 Architektur

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}"
Enter fullscreen mode Exit fullscreen mode

Erwartete Antwort:

{
  "resourceType": "CapabilityStatement",
  "status": "active",
  "date": "2026-03-25",
  "fhirVersion": "4.0.1",
  "rest": [{
    "mode": "server",
    "resource": [
      { "type": "Patient" },
      { "type": "Observation" },
      { "type": "Condition" }
    ]
  }]
}
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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]}`);
});
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

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'
});
Enter fullscreen mode Exit fullscreen mode

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}`);
});
Enter fullscreen mode Exit fullscreen mode

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}`);
});
Enter fullscreen mode Exit fullscreen mode

OAuth 2.0 und SMART on FHIR

FHIR-Authentifizierung

FHIR-Server nutzen OAuth 2.0 & OpenID Connect:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Client    │───▶│   Auth      │───▶│   FHIR      │
│   (App)     │    │   Server    │    │   Server    │
└─────────────┘    └─────────────┘    └─────────────┘
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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' }
    }
  }
]);
Enter fullscreen mode Exit fullscreen mode

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'
});
Enter fullscreen mode Exit fullscreen mode

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');
});
Enter fullscreen mode Exit fullscreen mode

Fehlerbehandlung

401 Nicht autorisiert

Symptome: "Nicht autorisiert"/"Ungültiges Token"

Lösungen:

  1. Token-Gültigkeit prüfen
  2. Scope korrekt?
  3. Authorization: Bearer {token} Header gesetzt?
  4. Stimmt Token-Audience zur FHIR-Server-URL?

403 Verboten

Symptome: Token gültig, aber Zugriff verweigert

Lösungen:

  1. Benutzerberechtigung prüfen
  2. Patientenkontext korrekt?
  3. SMART-Scopes passend?
  4. Zugriffskontrolle auf Ressourcen prüfen

404 Nicht gefunden

Symptome: Ressource nicht vorhanden, Endpunkt falsch

Lösungen:

  1. Ressourcen-ID korrekt?
  2. FHIR-Basis-URL korrekt?
  3. Ressourcentyp supported?
  4. 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}`);
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)