---
{
  "title": "@atomic-ehr/codegen: Python FHIR Types",
  "description": "Generieren Sie stark typisierte Pydantic-Modelle aus beliebigen FHIR-Paketen mit @atomic-ehr/codegen – inklusive Validierung, IDE-Unterstützung, polymorphen Bundles, primitiven Erweiterungen und fhirpy-Integration.",
  "date": "2026-04-20",
  "author": "Aleksandr Penskoi",
  "reading-time": "6 minutes",
  "tags": [
    "FHIR Tools",
    "FHIR Standard",
    "Code Generation",
    "Python"
  ]
}
---
[FHIR-Entwicklung](/articles/using-fhir-to-simplify-healthcare-application-development) in Python bedeutet in der Regel, mit rohen Dicts zu kämpfen, Feldnamen aus einer umfangreichen Spezifikation zu erraten und Tippfehler erst zur Laufzeit zu entdecken. Wir haben einen Generator entwickelt, der all das überflüssig macht.

[`@atomic-ehr/codegen`](https://github.com/atomic-ehr/codegen) erzeugt stark typisierte [Pydantic](https://docs.pydantic.dev/latest/)-Modelle aus beliebigen FHIR-Paketen – mit Validierung, IDE-Autovervollständigung und optionaler [fhirpy](https://github.com/beda-software/fhir-py)-Integration. Es funktioniert mit jedem FHIR-Server, nicht nur mit [Aidbox](https://www.health-samurai.io/aidbox). Das Projekt ist Open Source (MIT), und wir begrüßen Beiträge.

Dieser Beitrag aktualisiert unseren [früheren Python-SDK-Artikel](https://www.health-samurai.io/articles/type-schema-python-sdk-for-fhir). Seitdem wurde der Generator von Grund auf in einem einheitlichen TypeScript-Stack als `@atomic-ehr/codegen` neu geschrieben.

Was Sie erhalten:

- **Pydantic v2-Modelle** für jede Ressource und jeden Datentyp
- **Value Sets** – `Literal`-Enums und generisches `CodeableConcept[T]` mit Bindings im Typ
- **Primitive Erweiterungen** – typisierte `_birthDate`-, `_family`-Felder
- **[fhirpy Async-Client](https://github.com/beda-software/fhir-py)** – direkte Einbindung in `AsyncFHIRClient`
- **[Beliebige FHIR-Pakete](https://github.com/atomic-ehr/fhir-canonical-manager)** – R4, US Core und benutzerdefinierte Pakete in einem Durchlauf; Pakete können on the fly gepatcht werden
- **[Type Schema](https://www.health-samurai.io/articles/type-schema-a-pragmatic-approach-to-build-fhir-sdk)** – Tree Shaking, Promotion logischer Modelle, Kollisionsauflösung

## Generieren Sie Ihre FHIR-Typen

Installieren Sie den Generator (`bun add` und `pnpm add` funktionieren ebenfalls):

```bash
npm install @atomic-ehr/codegen
```

Erstellen Sie ein `generate.ts`-Skript:

```typescript
import { APIBuilder } from "@atomic-ehr/codegen";

const main = async () => {
  const builder = new APIBuilder()
    .fromPackage("hl7.fhir.r4.core", "4.0.1")
    .python({
      fieldFormat: "snake_case",
      allowExtraFields: false,
      primitiveTypeExtension: true,
    })
    .typeSchema({
      treeShake: {
        "hl7.fhir.r4.core": {
          "http://hl7.org/fhir/StructureDefinition/Patient": {},
          "http://hl7.org/fhir/StructureDefinition/Observation": {},
          "http://hl7.org/fhir/StructureDefinition/Bundle": {},
        },
      },
    })
    .outputTo("./fhir_types")
    .cleanOutput(true);

  const report = await builder.generate();
  if (!report.success) process.exit(1);
};

main();
```

Führen Sie es aus:

```bash
npx tsx generate.ts
```

Sie haben nun ein gebrauchsfertiges Python-Paket in `./fhir_types/`. Tree Shaking zieht automatisch nur die aufgelisteten Ressourcen sowie deren Abhängigkeiten ein (`DomainResource`, `Element`, `OperationOutcome` usw.).

## Was generiert wird

```
fhir_types/
├── __init__.py                  # Exports + model_rebuild() for forward refs
├── requirements.txt             # pydantic, requests, fhirpy, mypy, pytest
├── README.md
└── hl7_fhir_r4_core/
    ├── __init__.py
    ├── base.py                  # Complex types: CodeableConcept, HumanName, ...
    ├── patient.py               # Patient + nested types (PatientContact, ...)
    ├── observation.py
    ├── operation_outcome.py
    ├── bundle.py
    ├── domain_resource.py
    ├── resource.py
    └── resource_families.py     # Polymorphic validators for Bundle.entry etc.
```

Jede Ressource ist ein Pydantic-Modell mit korrekten Feldtypen, Aliases und Validierung:

```python
# Simplified -- other fields also carry serialization_alias, omitted for brevity
class Patient(DomainResource):
    model_config = ConfigDict(
        validate_by_name=True,
        serialize_by_alias=True,
        extra="forbid"
    )
    resource_type: Literal['Patient'] = Field(
        default='Patient', alias='resourceType',
        serialization_alias='resourceType', frozen=True
    )
    birth_date: str | None = Field(None, alias="birthDate")
    birth_date_extension: Element | None = Field(None, alias="_birthDate")
    gender: Literal["male", "female", "other", "unknown"] | None = Field(None)
    marital_status: CodeableConcept[
        Literal["A", "D", "I", "L", "M", "P", "S", "T", "U", "W", "UNK"] | str
    ] | None = Field(None, alias="maritalStatus")
    name: PyList[HumanName] | None = Field(None)  # PyList = typing.List
    # ...
```

Die [vollständig generierte Datei](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/hl7_fhir_r4_core/patient.py) enthält das vollständige Patient-Modell.

## Ressourcen erstellen und damit arbeiten

<details>
<summary>Python-Umgebung einrichten (Python 3.12+)</summary>

```bash
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r fhir_types/requirements.txt
```

</details>

Der Generator erzeugt **Typen und Validierung** – kein HTTP-Client ist enthalten. Für die Serverkommunikation verwenden Sie [fhirpy](https://github.com/beda-software/fhir-py) oder eine beliebige HTTP-Bibliothek. Hier arbeiten wir direkt mit den Modellen:

```python
from fhir_types.hl7_fhir_r4_core import Element, Extension, HumanName, Patient

patient = Patient(
    name=[HumanName(given=["John"], family="Doe")],
    gender="male",
    birth_date="1980-01-01",
    birth_date_extension=Element(
        extension=[
            Extension(
                url="http://hl7.org/fhir/StructureDefinition/patient-birthTime",
                value_date_time="1980-01-01T08:30:00-05:00",
            ),
        ],
    ),
)

# Serialize to FHIR JSON (camelCase keys, nulls excluded)
print(patient.to_json(indent=2))
```

```json
{
  "resourceType": "Patient",
  "birthDate": "1980-01-01",
  "_birthDate": {
    "extension": [{
      "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime",
      "valueDateTime": "1980-01-01T08:30:00-05:00"
    }]
  },
  "gender": "male",
  "name": [{ "family": "Doe", "given": ["John"] }]
}
```

```python
# Round-trip: JSON -> Patient
restored = Patient.from_json(patient.to_json())
assert restored == patient

# Typed field access
patient.gender                                              # 'male'
assert patient.name is not None
patient.name[0].family                                      # 'Doe'
patient.birth_date                                          # '1980-01-01'
assert patient.birth_date_extension is not None
assert patient.birth_date_extension.extension is not None
patient.birth_date_extension.extension[0].value_date_time   # '1980-01-01T08:30:00-05:00'
```

## Typprüfung und Validierung

Was passiert, wenn wir Fehler machen?

```python
from fhir_types.hl7_fhir_r4_core import HumanName, Patient

Patient(
    name=[HumanName(family="Doe")],
    gender="FOO",            # wrong value
    some_data="1990-01-01",  # wrong field
)
```

**Statische Analyse mit mypy.** Fügen Sie das Pydantic-Plugin zu `mypy.ini` hinzu:

```ini
[mypy]
python_version = 3.12
plugins = pydantic.mypy

[pydantic-mypy]
init_typed = True
```

Dann ausführen:

```bash
$ mypy . --strict
main.py:10: error: Unexpected keyword argument "some_data" for "Patient"  [call-arg]
main.py:12: error: Argument "gender" to "Patient" has incompatible type "Literal['FOO']"; expected "Literal['male', 'female', 'other', 'unknown'] | None"  [arg-type]
```

**Laufzeitvalidierung** – führen Sie das Skript aus, und Pydantic erkennt beide Fehler bei der Instanziierung:

```bash
$ python main.py
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Patient
gender
  Input should be 'male', 'female', 'other' or 'unknown'
some_data
  Extra inputs are not permitted
```

Es ist nicht notwendig, einen Validator separat auszuführen – Modelle erzwingen Constraints bei der Konstruktion. Wenn Sie zusätzliche Felder akzeptieren möchten, generieren Sie mit `allowExtraFields: true`.

## Anpassungsoptionen

| Option | Typ | Standard | Auswirkung |
|---|---|---|---|
| `fieldFormat` | `"snake_case"` \| `"camelCase"` | `"camelCase"` | Feldbenennung: `birth_date` vs. `birthDate` |
| `allowExtraFields` | `boolean` | `false` | Unbekannte Felder in der JSON-Eingabe akzeptieren |
| `primitiveTypeExtension` | `boolean` | `false` | Typisierte `_birthDate`-, `_family`-Begleitfelder generieren |
| `client` | `fhirpy`, `none` | `fhirpy` | [FhirpyBaseModel](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/fhirpy_base_model.py)-kompatible Basisklasse |

Diese Optionen lassen sich mit Funktionen auf `APIBuilder`-Ebene kombinieren – [Tree Shaking](#generieren-sie-ihre-fhir-typen), um nur die benötigten Ressourcen einzuschließen, [Multi-Paket-Laden](https://github.com/atomic-ehr/fhir-canonical-manager), um R4 Core mit IGs und benutzerdefinierten Profilen zu mischen, sowie [Paket-Vorverarbeitung](https://github.com/atomic-ehr/codegen#architecture), um fehlerhafte Upstream-Pakete vor der Generierung zu patchen.

## Als Nächstes: Profilunterstützung für Python

Unser TypeScript-Generator unterstützt bereits [FHIR-Profilklassen](https://github.com/atomic-ehr/codegen/blob/main/docs/posts/2026-03-09-typescript-profiles-quick.md) – generierte Klassen, die feste Werte automatisch befüllen, typisierte Accessoren für Slices und Erweiterungen bereitstellen und clientseitige Validierung beinhalten. Wir arbeiten daran, dieselben Funktionen für Python verfügbar zu machen:

- Slice-Accessoren mit automatisch angewendeten Diskriminatorwerten
- Getter/Setter für Erweiterungen
- Validierung mit strukturierten Fehlern und Warnungen

Wenn Sie daran interessiert sind – Feedback dazu, welche Profilmuster für Ihre Python-Workflows am wichtigsten sind, ist auf [GitHub](https://github.com/atomic-ehr/codegen) sehr willkommen.

## Über Python hinaus: Was `@atomic-ehr/codegen` sonst noch bietet

Dieser Beitrag konzentrierte sich auf den Python-Generator, aber `@atomic-ehr/codegen` ist eine mehrsprachige Code-Generierungsplattform. Hier ein Überblick über weitere Funktionen:

- **Vier integrierte Generatoren** – Python, TypeScript, C# und eine Mustache-Template-Engine für beliebige Sprachen
- **Paket-Vorverarbeitung** – reale FHIR-Pakete werden häufig mit Fehlern ausgeliefert: fehlerhafte kanonische URLs, fehlende CodeSystem-Konzepte, nicht verfügbare externe ValueSets. Der `preprocessPackage`-Hook ermöglicht es, diese vor der Generierung zu patchen, sodass Sie das Paket weder forken noch Fehler nachgelagert umgehen müssen
- **Promotion logischer Modelle** – logische StructureDefinitions in erstklassige Ressourcen für die Code-Generierung umwandeln
- **Schema-Kollisionsauflösung** – wenn mehrere Pakete überlappende Bindings definieren, können Sie festlegen, welche Quelle Vorrang hat
- **Multi-Source-Laden** – NPM-Pakete, lokale TGZ-Archive und einzelne StructureDefinition-JSON-Dateien in einem einzigen Generierungsdurchlauf kombinieren

Funktionierende Python-Beispiele:

- [Python + fhirpy](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4-us-core) – camelCase, asynchroner fhirpy-Client
- [Python + Pydantic + Custom Client](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4) – snake_case, primitive Erweiterungen, benutzerdefinierter synchroner Client

`@atomic-ehr/codegen` ist Open Source unter der MIT-Lizenz. Wenn Sie mit FHIR in Python arbeiten – ob Sie Datenpipelines, ML-Features oder klinische Anwendungen entwickeln – probieren Sie es aus und teilen Sie uns Ihre Meinung mit. Issues, Feature-Requests und Pull Requests sind herzlich willkommen.

[GitHub](https://github.com/atomic-ehr/codegen) | [NPM](https://www.npmjs.com/package/@atomic-ehr/codegen) | [Aleksandr Penskoi auf LinkedIn](https://www.linkedin.com/in/aleksandr-penskoi/)