Library that generates language-specific models from FHIR StructureDefinition using type-schema (Github), enabling developers to work with strongly-typed FHIR resources in their preferred programming language.
fhir-schema-codegen uses a two-step process:
-
Type-Schema Transformation: Converts FHIR StructureDefinition into type-schema format, which provides a flat and denormalized representation for easier data access.
-
Template-Based Generation: Uses generators to transform the type-schema into language-specific models for each supported language (TypeScript, C#, Python, etc.).
Supports custom template-based generators allowing you to add new language support, customize the output format of available generators, and implement language-specific features.
Join our community at FHIR Chat to learn more and contribute!
# Install globally
npm install -g @fhirschema/codegen
# Or use with npx
npx @fhirschema/codegen [command] [options]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 generatorsGenerates 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--with-debug-comment- Enable debug comments in generated code--profile- Enable profile generation
Example:
fscg generate -g typescript -o ./fhir.r4.sdk -p hl7.fhir.r4.core@4.0.1Lists of the available generators:
fscg generatorsCreates 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-generatorThe library supports multiple language generators, each providing type-safe FHIR resource handling. Below are the currently supported generators with examples and implementation details:
The TypeScript generator creates a fully typed SDK with interfaces for all FHIR resources. 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
# With debug comments:
fscg generate -g typescript -o ./ts-sdk -p hl7.fhir.r4.core@4.0.1 --with-debug-commentThe C# generator produces strongly-typed C# classes for FHIR resources. 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.1The Python generator creates Python classes. 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
# With options:
fscg generate -g python -o ./python-sdk -p hl7.fhir.r4.core@4.0.1 --py-sdk-package my_package --py-allow-extra-fieldsYou 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
Generator inherits from base Generator class and implements generate() method to produce target language code based on type-schema (see ./src/generators/typescript/index.ts)
For more information on creating and using custom generators, see the Generators Registry documentation.
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 fieldSymbol = fieldName;
if (!field.required) {
fieldSymbol += '?';
}
if (field.type.kind == 'primitive-type') {
type = typeMap[field.type.name] || '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[];
}Contributions are welcome! Please feel free to submit a Pull Request.
-
Clone the repository
git clone https://github.com/fhir-schema/fhir-schema-codegen.git cd fhir-schema-codegen -
Install dependencies
npm install
-
Build the project
npm run build
-
Run tests
npm test
To create a new generator for a language:
- Create a new directory in
src/generators/for your language - Create an
index.tsfile that exports acreateGeneratorfunction - Implement your generator by extending the base
Generatorclass - 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.