@fhirschema/codegen
TypeScript icon, indicating that this package has built-in type declarations

0.0.13 • Public • Published

fhir-schema codegen

NPM Version

Tests

Library that generates language-specific models from FHIR structure definitions using type-schema (flattened, denormalized structures for easier access).

How does it work?

fhir-schema-codegen uses a two-step process:

  1. Type Schema Transformation: Converts FHIR structure definitions into type-schema format, which provides a flat and denormalized representation of FHIR resources.

  2. Template-Based Generation: Uses generators to transform the type-schema into language-specific models for each supported language (TypeScript, C#, Python, etc.).

Supports custom generators allowing you to:

  • add new language support
  • customize the output format of available generators
  • implement language-specific features

Join our community at FHIR Chat to learn more and contribute!

Installation

# Install globally
npm install -g @fhirschema/codegen

# Or use with npx
npx @fhirschema/codegen [command] [options]

Usage

The fhir-schema codegen provides several commands to work with FHIR definitions and generate code:

# Generate code
fscg generate -g typescript -o /fhir.r4.sdk -p hl7.fhir.r4.core@4.0.1

# List all available (built in) generators
fscg generators

Command Reference

generate

Generates code from core FHIR Implementation Guide:

fscg generate -g <generator> -o <output-dir> -p <fhir.package@version>

Options:

  • -g, --generator <generator> - Generator to use (typescript, csharp, python)
  • -o, --output <directory> - Output directory
  • -p, --packages <packages...> - Available published FHIR IGs
  • -f, --files <files...> - TypeSchema source *.ndjson files
  • --custom-generator <path> - Path to your custom generator template

Example:

fscg generate -g typescript -o ./fhir.r4.sdk -p hl7.fhir.r4.core@4.0.1

Create a custom generator template

generators

Lists of the available generators:

fscg generators

create-generator

Creates a new custom generator template:

fscg create-generator -o <output-directory>

Options:

  • -o, --output <directory> - Output directory (default: ./fhirschema-generators)

Example:

fscg create-generator -o ./my-generator

Supported Generators

The fhir-schema codegen supports multiple language generators, each providing type-safe FHIR resource handling. Below are the currently supported generators with examples and implementation details:

TypeScript Generator

The TypeScript generator creates a fully typed SDK with interfaces for all FHIR resources. It includes:

  • Type definitions for all FHIR resources and data types
  • Type-safe client implementation for FHIR operations
  • Proper TypeScript module structure with exports

Example implementation in ./example/typescript/:

import { Patient } from './aidbox/types/hl7-fhir-r4-core';

const patient: Patient = {
  identifier: [{ system: 'http://org.io/id', value: '0000-0000' }],
  name: [{ given: ['John'], family: 'Doe' }],
  gender: 'male',
  birthDate: '1990-01-01',
};

Generate TypeScript SDK:

fscg generate -g typescript -o ./ts-sdk -p hl7.fhir.r4.core@4.0.1

C# Generator

The C# generator produces strongly-typed C# classes for FHIR resources with:

  • C# classes for all FHIR resources and data types
  • Proper namespace organization
  • Serialization attributes for JSON handling

Example implementation in ./example/csharp/:

using Aidbox.FHIR.R4.Core;

var patient = new Patient
{
    Identifier = [new Identifier { System = "http://hl7.org/fhir/us/CodeSystem/identity", Value = "0000-0000" }],
    Name = [new HumanName { Given = ["John"], Family = "Doe" }],
    Gender = "male",
    BirthDate = "1990-01-01",
};

Generate C# SDK:

fscg generate -g csharp -o ./csharp-sdk -p hl7.fhir.r4.core@4.0.1

Python Generator

The Python generator creates Python classes with:

  • Python classes for FHIR resources and data types
  • Type hints for better IDE support
  • JSON serialization/deserialization support
  • Structured authentication handling

Example implementation in ./example/python/:

from aidbox.hl7_fhir_r4_core.base import HumanName, Identifier
from aidbox.hl7_fhir_r4_core import Patient

patient = Patient(
    identifier=[Identifier(system="http://org.io/id", value="0000-0000")],
    name=[HumanName(given=["John"], family="Doe")],
    gender="male",
    birth_date="1990-01-01",
)

Generate Python SDK:

fscg generate -g python -o ./python-sdk -p hl7.fhir.r4.core@4.0.1

Custom Generators

You can create custom generators to support additional languages or specialized formats. The generator system is extensible and allows you to:

  • Create generators for new languages
  • Customize the output format
  • Add language-specific features

For more information on creating and using custom generators, see the Generators Registry documentation.

How it works

  1. Loader loads source package and canonicals (file or fhir package)
  2. Transform it to type-schema
  3. Generator inherits from base Generator class and implements generate() method to produce target language code based on type-schema (see typescript.ts) Generator may define additional options and use conditional generation logic.
  4. Generator should be registered in CLI utility to be available in CLI.

TypeScript Example

import { Generator, type GeneratorOptions, NestedTypeSchema, TypeSchema } from '@fhirschema/codegen';
import path from 'node:path';

export interface CustomGeneratorOptions extends GeneratorOptions {
    // Add custom options here
}

const typeMap :  Record<string, string> = {
    'boolean': 'boolean',
    'integer': 'number',
    'decimal': 'number',
    'positiveInt': 'number',
    'number': 'number'
}

export class CustomGenerator extends Generator {
    constructor(opts: CustomGeneratorOptions) {
        super({
            ...opts,
            staticDir: path.resolve(__dirname, '../static'),
        });
    }

    generateType(schema: TypeSchema | NestedTypeSchema) {
        let base = schema.base ? 'extends ' + schema.base.name : '';
        this.curlyBlock(['export', 'interface', schema.identifier.name, base], () => {
            if (schema.fields) {
                for (const [fieldName, field] of Object.entries(schema.fields)) {
                    if ('choices' in field) continue;
                    let tp = field.type.name;
                    let type = tp;
                    let fieldSymbol = fieldName;
                    if (!field.required) {
                        fieldSymbol += '?';
                    }
                    if (field.type.kind == 'primitive-type') {
                        type = typeMap[tp] || 'string'
                    } else {
                        type = field.type.name;
                    }
                    this.lineSM(fieldSymbol, ':', type + (field.array ? '[]' : ''));
                }
            }
        });
        this.line();
    }

    generate() {
        this.dir('src', async () => {
            this.file('types.ts', () => {
                for (let schema of this.loader.complexTypes()) {
                    this.generateType(schema);
                }
            });

            for (let schema of this.loader.resources()) {
                this.file(schema.identifier.name + ".ts", () => {
                    if (schema.dependencies) {
                        for (let dep of schema.dependencies.filter(d => d.kind == 'complex-type')) {
                            this.lineSM('import', '{', dep.name, '}', 'from', '"./types.ts"');
                        }

                        for (let dep of schema.dependencies.filter(d => d.kind == 'resource')) {
                            this.lineSM('import', '{', dep.name, '}', 'from', '"./' + dep.name + '.ts"');
                        }
                    }

                    this.line();

                    if (schema.nested) {
                        for (let subtype of schema.nested) {
                            this.generateType(subtype);
                        }
                    }

                    this.line();

                    this.generateType(schema);
                });
            }
        })
    }
}

export function createGenerator(options: GeneratorOptions): Generator {
    return new CustomGenerator(options);
}

This will produce something like this:

import { CodeableConcept } from "./types.ts";
import { Reference } from "./types.ts";
import { HumanName } from "./types.ts";
import { Address } from "./types.ts";
import { Identifier } from "./types.ts";
import { Attachment } from "./types.ts";
import { BackboneElement } from "./types.ts";
import { ContactPoint } from "./types.ts";
import { Period } from "./types.ts";
import { DomainResource } from "./DomainResource.ts";

export interface PatientLink extends BackboneElement {
  other : Reference;
  type : string;
}

export interface PatientCommunication extends BackboneElement {
  language : CodeableConcept;
  preferred? : boolean;
}

export interface PatientContact extends BackboneElement {
  address? : Address;
  gender? : string;
  name? : HumanName;
  organization? : Reference;
  period? : Period;
  relationship? : CodeableConcept[];
  telecom? : ContactPoint[];
}


export interface Patient extends DomainResource {
  active? : boolean;
  address? : Address[];
  birthDate? : string;
  communication? : PatientCommunication[];
  contact? : PatientContact[];
  deceasedBoolean? : boolean;
  deceasedDateTime? : string;
  gender? : string;
  generalPractitioner? : Reference[];
  identifier? : Identifier[];
  link? : PatientLink[];
  managingOrganization? : Reference;
  maritalStatus? : CodeableConcept;
  multipleBirthBoolean? : boolean;
  multipleBirthInteger? : number;
  name? : HumanName[];
  photo? : Attachment[];
  telecom? : ContactPoint[];
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

  1. Clone the repository

    git clone https://github.com/fhir-schema/fhir-schema-codegen.git
    cd fhir-schema-codegen
  2. Install dependencies

    npm install
  3. Build the project

    npm run build
  4. Run tests

    npm test

Creating a New Generator

To create a new generator for a language:

  1. Create a new directory in src/generators/ for your language
  2. Create an index.ts file that exports a createGenerator function
  3. Implement your generator by extending the base Generator class
  4. Add your generator to the choices in the CLI in src/cli.ts

Alternatively, you can use the create-generator command to create a custom generator outside the main codebase.

Regenerating the SDK

If you need to regenerate the SDK with updated FHIR definitions:

  1. Update the FHIR definitions in the source
  2. Build the generator
  3. Run the generation command:
cd fhir-schema-codegen

npm run build

node dist/cli.js generate --generator typescript --output ./example/typescript/aidbox  --packages hl7.fhir.r4.core@4.0.1

/@fhirschema/codegen/

    Package Sidebar

    Install

    npm i @fhirschema/codegen

    Weekly Downloads

    9

    Version

    0.0.13

    License

    ISC

    Unpacked Size

    198 kB

    Total Files

    52

    Last publish

    Collaborators

    • niquola