---
{
  "title": "@atomic-ehr/codegen: Python FHIR Types",
  "description": "Genere modelos Pydantic fuertemente tipados a partir de cualquier paquete FHIR con @atomic-ehr/codegen — validación, soporte de IDE, bundles polimórficos, extensiones de tipos primitivos e integración con fhirpy incluidas.",
  "date": "2026-04-20",
  "author": "Aleksandr Penskoi",
  "reading-time": "6 minutes",
  "tags": [
    "FHIR Tools",
    "FHIR Standard",
    "Code Generation",
    "Python"
  ]
}
---

> For the complete documentation index, see [llms.txt](https://www.health-samurai.io/llms.txt).
> Use it to discover all available pages before guessing URLs.

---
El [desarrollo FHIR](/articles/using-fhir-to-simplify-healthcare-application-development) en Python suele implicar lidiar con diccionarios sin tipo, adivinar nombres de campo en una especificación enorme y descubrir errores tipográficos solo en tiempo de ejecución. Hemos creado un generador que elimina todo eso.

[`@atomic-ehr/codegen`](https://github.com/atomic-ehr/codegen) produce modelos [Pydantic](https://docs.pydantic.dev/latest/) fuertemente tipados a partir de cualquier paquete FHIR — con validación, autocompletado en el IDE e integración opcional con [fhirpy](https://github.com/beda-software/fhir-py). Funciona con cualquier servidor FHIR, no solo con [Aidbox](https://www.health-samurai.io/aidbox). El proyecto es de código abierto (MIT) y agradecemos las contribuciones.

Esta entrada actualiza nuestro [artículo anterior sobre el SDK de Python](https://www.health-samurai.io/articles/type-schema-python-sdk-for-fhir). Desde entonces, el generador ha sido reescrito desde cero en un único stack TypeScript como `@atomic-ehr/codegen`.

Lo que obtendrá:

- **Modelos Pydantic v2** para cada recurso y tipo de dato
- **Value sets** — enumeraciones `Literal` y `CodeableConcept[T]` genérico con bindings en el tipo
- **Extensiones de tipos primitivos** — campos `_birthDate`, `_family` tipados
- **[Cliente asíncrono fhirpy](https://github.com/beda-software/fhir-py)** — conéctese directamente a `AsyncFHIRClient`
- **[Cualquier paquete FHIR](https://github.com/atomic-ehr/fhir-canonical-manager)** — R4, US Core y personalizados en un solo paso; parchee paquetes al vuelo
- **[Type Schema](https://www.health-samurai.io/articles/type-schema-a-pragmatic-approach-to-build-fhir-sdk)** — tree shaking, promoción de modelos lógicos y resolución de colisiones

## Genere Sus Tipos FHIR

Instale el generador (`bun add` y `pnpm add` también funcionan):

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

Cree un script `generate.ts`:

```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();
```

Ejecútelo:

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

Ahora tendrá un paquete Python listo para usar en `./fhir_types/`. El tree shaking incluye únicamente los recursos que ha listado, junto con sus dependencias (`DomainResource`, `Element`, `OperationOutcome`, etc.) de forma automática.

## Qué Se Genera

```
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.
```

Cada recurso es un modelo Pydantic con tipos de campo correctos, alias y validación:

```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
    # ...
```

Consulte el [archivo generado completo](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/hl7_fhir_r4_core/patient.py) para ver el modelo Patient completo.

## Crear y Trabajar con Recursos

<details>
<summary>Configurar el entorno Python (Python 3.12+)</summary>

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

</details>

El generador produce **tipos y validación** — no incluye ningún cliente HTTP. Para la comunicación con el servidor, use [fhirpy](https://github.com/beda-software/fhir-py) o cualquier biblioteca HTTP. Aquí trabajamos directamente con los modelos:

```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'
```

## Comprobación de Tipos y Validación

¿Qué ocurre cuando cometemos errores?

```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
)
```

**Análisis estático con mypy.** Añada el plugin de Pydantic a `mypy.ini`:

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

[pydantic-mypy]
init_typed = True
```

A continuación, ejecútelo:

```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]
```

**Validación en tiempo de ejecución** — ejecute el script y Pydantic detecta ambos errores en la instanciación:

```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
```

No es necesario ejecutar un validador por separado — los modelos aplican las restricciones en la construcción. Si necesita aceptar campos adicionales, genere con `allowExtraFields: true`.

## Opciones de Personalización

| Opción | Tipo | Valor por defecto | Efecto |
|---|---|---|---|
| `fieldFormat` | `"snake_case"` \| `"camelCase"` | `"camelCase"` | Nomenclatura de campos: `birth_date` frente a `birthDate` |
| `allowExtraFields` | `boolean` | `false` | Acepta campos desconocidos en la entrada JSON |
| `primitiveTypeExtension` | `boolean` | `false` | Genera campos complementarios tipados `_birthDate`, `_family` |
| `client` | `fhirpy`, `none` | `fhirpy` | Clase base compatible con [FhirpyBaseModel](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/fhirpy_base_model.py) |

Estas opciones se combinan con las funcionalidades a nivel de `APIBuilder` — [tree shaking](#genere-sus-tipos-fhir) para incluir solo los recursos que necesita, [carga de múltiples paquetes](https://github.com/atomic-ehr/fhir-canonical-manager) para mezclar el núcleo R4 con IGs y perfiles personalizados, y [preprocesamiento de paquetes](https://github.com/atomic-ehr/codegen#architecture) para parchear paquetes upstream defectuosos antes de la generación.

## Próximamente: Soporte de Perfiles para Python

Nuestro generador TypeScript ya admite [clases de perfil FHIR](https://github.com/atomic-ehr/codegen/blob/main/docs/posts/2026-03-09-typescript-profiles-quick.md) — clases generadas que rellenan automáticamente valores fijos, proporcionan accesores tipados para slices y extensiones, e incluyen validación del lado del cliente. Estamos trabajando en llevar las mismas capacidades a Python:

- Accesores de slice con valores discriminadores aplicados automáticamente
- Getters/setters de extensiones
- Validación que devuelve errores y advertencias estructurados

Si le interesa esto, agradecemos sus comentarios sobre qué patrones de perfil son más relevantes para sus flujos de trabajo en Python en [GitHub](https://github.com/atomic-ehr/codegen).

## Más Allá de Python: Qué Más Hace @atomic-ehr/codegen

Esta entrada se ha centrado en el generador de Python, pero `@atomic-ehr/codegen` es una plataforma de generación de código multilenguaje. Esto es lo que más ofrece:

- **Cuatro generadores integrados** — Python, TypeScript, C# y un motor de plantillas Mustache para cualquier lenguaje
- **Preprocesamiento de paquetes** — los paquetes FHIR del mundo real suelen incluir errores: URLs canónicas malformadas, conceptos de CodeSystem faltantes, ValueSets externos no disponibles. El hook `preprocessPackage` permite parchearlos antes de la generación, de modo que no tenga que bifurcar el paquete ni solucionar errores más adelante
- **Promoción de modelos lógicos** — convierte StructureDefinitions lógicas en recursos de primera clase para la generación de código
- **Resolución de colisiones de esquema** — cuando varios paquetes definen bindings superpuestos, puede especificar qué fuente tiene prioridad
- **Carga de múltiples fuentes** — combine paquetes NPM, archivos TGZ locales y archivos JSON de StructureDefinition sueltos en un único paso de generación

Ejemplos de Python en funcionamiento:

- [Python + fhirpy](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4-us-core) — camelCase, cliente fhirpy asíncrono
- [Python + Pydantic + Cliente personalizado](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4) — snake_case, extensiones de tipos primitivos, cliente síncrono personalizado

`@atomic-ehr/codegen` es de código abierto bajo la licencia MIT. Si trabaja con FHIR en Python — ya sea desarrollando pipelines de datos, funcionalidades de ML o aplicaciones clínicas — pruébelo y cuéntenos qué le parece. Las incidencias, las solicitudes de funcionalidades y los PRs son bienvenidos.

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