Skip to main content

CLI Usage

The @fhir-dsl/cli package provides the fhir-gen command for generating TypeScript types from FHIR StructureDefinitions.

Installation​

# Install globally
npm install -g @fhir-dsl/cli

# Or as a dev dependency
npm install -D @fhir-dsl/cli

# Or run directly with npx
npx @fhir-dsl/cli generate --version r4 --out ./src/fhir

Commands​

fhir-gen generate​

Generates TypeScript types from FHIR specifications.

fhir-gen generate [options]

Options​

OptionDescriptionRequired
--version <version>FHIR version: r4, r4b, r5, r6Yes
--out <dir>Output directory for generated filesYes
--ig <packages...>Implementation Guide packages to includeNo
--resources <list>Comma-separated list of resource namesNo
--src <path>Local FHIR definitions directory (skips download)No
--cache <dir>Cache directory for downloaded specsNo
--expand-valuesetsGenerate typed unions from FHIR ValueSet bindingsNo
--resolve-codesystemsGenerate CodeSystem namespace objects for IntelliSenseNo
--include-specEmit markdown spec files alongside types (for AI/LLM context)No
--validator <target>Emit Standard Schema runtime validators: native or zodNo
--strict-extensibleTreat extensible bindings as closed enums (validator only)No

Examples​

Basic R4 Generation​

Generate types for all FHIR R4 resources:

fhir-gen generate --version r4 --out ./src/fhir

With US Core Profiles​

Include US Core Implementation Guide profiles:

fhir-gen generate \
--version r4 \
--ig hl7.fhir.us.core@6.1.0 \
--out ./src/fhir

This generates additional profile interfaces with narrowed types (e.g., USCorePatientProfile where gender is required).

Specific Resources Only​

Generate types for a subset of resources:

fhir-gen generate \
--version r4 \
--resources Patient,Observation,Encounter,Condition \
--out ./src/fhir

This significantly reduces the size of generated output when you only need a few resource types.

With Terminology Types​

Generate types with compile-time coded value validation:

fhir-gen generate \
--version r4 \
--expand-valuesets \
--resolve-codesystems \
--out ./src/fhir

This generates a terminology/ directory with literal union types (e.g., AdministrativeGender = "male" | "female" | "other" | "unknown") and parameterizes bound fields like Patient.gender as FhirCode<AdministrativeGender>. See Terminology Engine for details.

With Runtime Validators​

Emit Standard Schema validators alongside the types so you can check incoming payloads at runtime:

fhir-gen generate \
--version r4 \
--expand-valuesets \
--validator native \
--out ./src/fhir

--validator native produces a zero-dependency implementation; --validator zod emits Zod schemas and requires zod as a peer dependency. Both conform to Standard Schema V1, so call sites (schema["~standard"].validate(value)) stay identical if you switch targets. See Validation for the full guide, including profile schemas.

With Markdown Spec (for AI assistants)​

Emit a parallel tree of markdown files summarizing every generated resource and profile:

fhir-gen generate \
--version r4 \
--resources Patient,Observation \
--include-spec \
--out ./src/fhir

This writes <out>/<version>/spec/ alongside the TypeScript output:

src/fhir/r4/spec/
index.md # Linked index of every resource + profile
resources/
patient.md # Description, properties table, backbone elements, search params
observation.md
...
profiles/ # Only when --ig is used
uscore-patient.md # Same shape, listing only constrained deltas vs. base
...

Each resource file contains the resource description, a properties table with cardinality / type / terminology binding / description for every field, nested tables for each backbone element, and a table of search parameters. Intended as context for AI coding assistants working against the generated types — point the tool at spec/ (or attach spec/index.md) so it can reason about FHIR semantics, not just TypeScript shapes.

Built purely from data the generator already parses — no extra network calls, works offline with --src, and respects --resources so the spec stays in lockstep with the emitted types.

Using Local Definitions​

If you have FHIR StructureDefinitions locally (e.g., for offline builds):

fhir-gen generate \
--version r4 \
--src ./my-fhir-definitions \
--out ./src/fhir

Custom Cache Directory​

Control where downloaded specs are cached:

fhir-gen generate \
--version r4 \
--cache ./node_modules/.cache/fhir \
--out ./src/fhir

Generated Output​

The CLI produces a directory structure under <out>/<version>/:

src/fhir/
r4/
index.ts # FhirSchema type + re-exports
client.ts # createClient() helper
registry.ts # FhirResourceMap, SearchParamRegistry, etc.
search-params.ts # Typed search parameters per resource
search-param-types.ts # Search parameter type definitions
resources/
patient.ts # Patient interface
observation.ts # Observation interface
encounter.ts # Encounter interface
... # One file per resource
terminology/ # Only when --expand-valuesets is used
valuesets.ts # Literal union types for ValueSet bindings
codesystems.ts # CodeSystem const objects (with --resolve-codesystems)
index.ts
profiles/ # Only when --ig is used
uscore-patient-profile.ts
uscore-vital-signs-profile.ts
...
index.ts
profile-registry.ts
schemas/ # Only when --validator is used
__runtime.ts # native adapter only
datatypes.ts
terminology.ts
resources/
patient.schema.ts
...
profiles/ # Only when --ig is also used
uscore-patient-profile.schema.ts
profile-schema-registry.ts
index.ts
spec/ # Only when --include-spec is used
index.md
resources/
patient.md
...
profiles/ # Only when --ig is also used
uscore-patient.md
...

Key Generated Files​

index.ts​

Re-exports everything and defines the GeneratedSchema type:

export type GeneratedSchema = {
resources: FhirResourceMap;
searchParams: SearchParamRegistry;
includes: IncludeRegistry;
revIncludes: RevIncludeRegistry;
profiles: ProfileRegistry;
};

client.ts​

A pre-typed client factory:

import { createFhirClient, type FhirClientConfig } from "@fhir-dsl/core";
import type { GeneratedSchema } from "./index";

export function createClient(config: FhirClientConfig) {
return createFhirClient<GeneratedSchema>(config);
}

Use this instead of manually passing the schema type:

// Instead of this:
import { createFhirClient } from "@fhir-dsl/core";
import type { GeneratedSchema } from "./fhir/r4";
const fhir = createFhirClient<GeneratedSchema>({ baseUrl: "..." });

// Use this:
import { createClient } from "./fhir/r4";
const fhir = createClient({ baseUrl: "..." });

registry.ts​

Maps resource type names to their interfaces:

export interface FhirResourceMap {
Patient: Patient;
Observation: Observation;
// ... all resources
}

export interface SearchParamRegistry {
Patient: PatientSearchParams;
Observation: ObservationSearchParams;
// ...
}

export interface IncludeRegistry {
Patient: {
"general-practitioner": "Practitioner" | "PractitionerRole";
organization: "Organization";
};
// ...
}

export interface RevIncludeRegistry {
Patient: {
Observation: "subject" | "performer";
Encounter: "subject";
// ... every resource+param that targets Patient
};
// ...
}

Adding to Your Build​

Add generation as a script in your package.json:

{
"scripts": {
"generate:fhir": "fhir-gen generate --version r4 --ig hl7.fhir.us.core@6.1.0 --out ./src/fhir",
"build": "npm run generate:fhir && tsc"
}
}
tip

Generated files should be committed to your repository. This avoids requiring the FHIR spec download during CI builds and ensures type consistency across your team.

Supported FHIR Versions​

VersionStatus
R4 (4.0.1)Fully supported
R4B (4.3.0)Supported
R5 (5.0.0)Supported
R6Supported