Library that generates language-specific models from FHIR structure definitions using type-schema (flattened, denormalized structures for easier access).
fhir-schema-codegen uses a two-step process:
-
Type Schema Transformation: Converts FHIR structure definitions into type-schema format, which provides a flat and denormalized representation of FHIR resources.
-
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!
# 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 generators
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
Lists of the available generators:
fscg generators
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
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:
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
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
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
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.
- Loader loads source package and canonicals (file or fhir package)
- Transform it to type-schema
- 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.
- Generator should be registered in CLI utility to be available in CLI.
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[];
}
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.ts
file that exports acreateGenerator
function - Implement your generator by extending the base
Generator
class - 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.
If you need to regenerate the SDK with updated FHIR definitions:
- Update the FHIR definitions in the source
- Build the generator
- 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