|
<< Klicken Sie, um das Inhaltsverzeichnis anzuzeigen >> Navigation: Schnittstellen > FHIR Server (+) > Schreiben |
Der FHIR Server nimmt eingehende Daten als Observation (Einzelwerte) und QuestionnaireResponse (Formulare) an. Drei Eigenschaften vorweg:
1.Patienten werden nicht angelegt. Stammdaten werden ausschließlich in EMIL gepflegt. POST /Patient und PUT /Patient/<id> antworten mit 405 Method Not Allowed. Ermitteln Sie zuerst per GET /Patient?identifier=... die interne ID.
2.Whitelist pro Client. Geschrieben werden dürfen nur ItemType-IDs, die in der Spalte Erlaubte ItemTypen (inbound) des Clients freigegeben sind (siehe Clients verwalten). Standard ist leer, also kein Schreibrecht.
3.Idempotent. Ein POST /Observation mit (Subject, Visit-Datum, ItemType), zu dem schon ein Item existiert, überschreibt das bestehende. Mehrfaches Senden desselben Bodys ist sicher.
Sie wollen für einen Patienten an einem bestimmten Datum das Körpergewicht (ItemType 10038) eintragen.
GET /fhir/CodeSystem/itemtype/?_count=2000
Im Ergebnis nach „Körpergewicht“ suchen (oder den ItemType im Datendictionary nachsehen):
{ "code": "10038",
"display": "Körpergewicht",
"property": [
{ "code": "area", "valueCode": "V" },
{ "code": "unit", "valueString": "kg" },
{ "code": "variable", "valueString": "GEWICHT" },
{ "code": "kind", "valueCode": "number" }
]
}
Das heißt: ItemType 10038, Bereich V (Verlauf), Einheit kg, Datentyp number. Der Wert muss also als valueQuantity mit Einheit kg kommen.
Unter Administration|Schnittstellen|FHIR R4 Server beim betreffenden Client in der Spalte Erlaubte ItemTypen (inbound) klicken, im Auswahldialog „Körpergewicht“ ankreuzen, OK. Die Freigabe wirkt sofort (siehe Wann Änderungen wirken).
GET /fhir/Patient?identifier=K12345
Authorization: Bearer <token>
→ aus der Antwort die id entnehmen, z. B. 42.
POST /fhir/Observation
Authorization: Bearer <token>
Content-Type: application/fhir+json
{
"resourceType": "Observation",
"status": "final",
"subject": { "reference": "Patient/42" },
"code": {
"coding": [{
"system": "https://emil.itc-ms.de/fhir/sid/itemtype",
"code": "10038"
}]
},
"effectiveDateTime": "2026-05-17",
"valueQuantity": {
"value": 72.5,
"unit": "kg",
"system": "http://unitsofmeasure.org"
}
}
Antwort: 201 Created, Header Location: .../Observation/<itemid> und Body:
{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "information",
"code": "informational",
"diagnostics": "Observation gespeichert: Patient/42, Encounter/142862, Observation/2247369."
}]
}
Die Item-ID (hier 2247369) können Sie für spätere Updates per PUT merken. Den Encounter (142862) hat der Server am Datum gefunden oder neu angelegt. Der Wert erscheint sofort im Client; der Audit-Trail verzeichnet den Client als Urheber.
Ist keine encounter.reference angegeben, sucht der Server eine vorhandene Visit am effectiveDateTime-Datum für das Subject, im passenden Bereich (V für normale Visits, L für Labor, F für Stammdaten). Existiert keine, legt er sie an. Mehrere Observations zum selben Datum landen damit in derselben Visit.
Alternativ können Sie eine bestehende Visit gezielt referenzieren, wenn Sie deren ID kennen (z. B. aus einem vorherigen Bundle-Export); sie muss zum Patienten gehören:
"encounter": { "reference": "Encounter/142862" }
Für ItemTypes mit kind=slist oder kind=mlist verwendet der Body valueCodeableConcept mit einem oder mehreren coding[]-Einträgen. Die Codes haben die Form <typeid>/<rawcode> wie im CodeSystem dokumentiert; das $-Präfix aus der Datenbank wird nicht mitgeschickt.
Beispiel für eine Mehrfachauswahl „Allergien“ (fiktiver ItemType 12345 aus dem CodeSystem-Beispiel) mit zwei gewählten Codes:
POST /fhir/Observation
Authorization: Bearer <token>
Content-Type: application/fhir+json
{
"resourceType": "Observation",
"status": "final",
"subject": { "reference": "Patient/42" },
"code": {
"coding": [{
"system": "https://emil.itc-ms.de/fhir/sid/itemtype",
"code": "12345"
}]
},
"effectiveDateTime": "2026-05-17",
"valueCodeableConcept": {
"coding": [
{ "system": "https://emil.itc-ms.de/fhir/sid/itemtype", "code": "12345/POLLEN" },
{ "system": "https://emil.itc-ms.de/fhir/sid/itemtype", "code": "12345/PEANUT" }
]
}
}
Für kind=slist (Einfachauswahl): genau ein coding[]-Eintrag.
Textfelder (kind=text…) erhalten den Wert als valueString, hier „Sonstige Befunde“ (ItemType 10080):
POST /fhir/Observation
Authorization: Bearer <token>
Content-Type: application/fhir+json
{
"resourceType": "Observation",
"status": "final",
"subject": { "reference": "Patient/42" },
"code": { "coding": [{ "system": "https://emil.itc-ms.de/fhir/sid/itemtype", "code": "10080" }] },
"effectiveDateTime": "2026-05-17",
"valueString": "Patient klagt über Müdigkeit nach Belastung."
}
Den Wert eines bereits geschriebenen Items ändern:
PUT /fhir/Observation/2247369
Authorization: Bearer <token>
Content-Type: application/fhir+json
{ ... gleicher Body wie oben, mit geändertem value ... }
→ 200 OK. Bei nicht existierender ID: 404 Not Found (kein Update-as-Create).
POST oder PUT? Für normale Schreibvorgänge reicht POST, er ist idempotent (gleicher Body, gleicher Endzustand). PUT brauchen Sie nur, wenn Sie ausdrücklich ein bestimmtes Item per ID ansprechen wollen.
Für Form-ItemTypes (kind form/calcform/custform/custhform) wird jedes Formularfeld ein item[]-Eintrag mit linkId und answer[0].value[x]. Vorgehen wie bei der Observation:
1.ItemType-ID nachschlagen (CodeSystem/itemtype, kind-Property prüfen).
2.Den ItemType in der Spalte Erlaubte ItemTypen (inbound) des Clients freischalten.
3.Patient-ID besorgen.
4.POST schicken.
Die linkId sind die Feldnamen des Formulars; beim HAQ sind es die Auswahlfelder rbN (angekreuzte Felder speichert der Server als "1", siehe Checkboxen unten).
POST /fhir/QuestionnaireResponse
Authorization: Bearer <token>
Content-Type: application/fhir+json
{
"resourceType": "QuestionnaireResponse",
"status": "completed",
"questionnaire": "Questionnaire/50123",
"subject": { "reference": "Patient/42" },
"authored": "2026-05-17",
"item": [
{ "linkId": "rb2", "answer": [{ "valueBoolean": true }] },
{ "linkId": "rb10", "answer": [{ "valueBoolean": true }] },
{ "linkId": "rb19", "answer": [{ "valueBoolean": true }] }
]
}
Bei kind=calcform (wie dem HAQ) berechnet der Server den Score (im Speicherformat vor #255) selbst; ein mitgesendetes linkId="score" wird bei der nächsten Neuberechnung überschrieben.
Für Form-Items, in denen mehrere strukturierte Einträge untereinander stehen (z. B. eine Medikamentenliste): oberste item[]-Einträge mit linkId="row" und verschachteltem item[] je Zeile. Beispiel „RheCORD Medikation“ (ItemType 52086, Felder name und instructions):
POST /fhir/QuestionnaireResponse
Authorization: Bearer <token>
Content-Type: application/fhir+json
{
"resourceType": "QuestionnaireResponse",
"status": "completed",
"questionnaire": "Questionnaire/52086",
"subject": { "reference": "Patient/42" },
"authored": "2026-05-17",
"item": [
{ "linkId": "row", "item": [
{ "linkId": "name", "answer": [{ "valueString": "Methotrexat" }] },
{ "linkId": "instructions", "answer": [{ "valueString": "15 mg 1x wöchentlich s.c." }] }
]},
{ "linkId": "row", "item": [
{ "linkId": "name", "answer": [{ "valueString": "Folsäure" }] },
{ "linkId": "instructions", "answer": [{ "valueString": "5 mg 1x wöchentlich" }] }
]}
]
}
Checkboxen stehen im Speicherformat nur, wenn sie angekreuzt sind. Beim Schreiben:
{ "linkId": "cbprednisolon", "answer": [{ "valueBoolean": true }] }
→ landet als "cbprednisolon": "1" im Storage. false oder Weglassen führt dazu, dass der Eintrag nicht erzeugt wird.
Fest verdrahtete Schnittstellen (RheCORD) liefern Ergebnisse per POST /fhir mit Bundle.type=transaction (alternativ POST /fhir/$transaction). Das Bundle enthält eine minimale Patient-Ressource (Identifier im aktiven System laut $pid-config, dazu Nach- und Vorname zum Abgleich) und eine oder mehrere QuestionnaireResponse und/oder Observation, deren subject.reference per urn:uuid auf den Patient-Eintrag zeigt.
•Patienten werden nur aufgelöst, nicht angelegt. Der Namensabgleich (Vollname) unterscheidet nicht zwischen Groß- und Kleinschreibung; das Geburtsdatum wird nicht geprüft.
•Ist der Identifier nicht auflösbar oder weicht der Name ab, wird das gesamte Bundle mit einem OperationOutcome abgelehnt; es entsteht keine verwaiste QuestionnaireResponse.
•Antwort: Bundle.type=transaction-response mit response.status je Eintrag. Bundle.type=batch wird nicht verarbeitet.