A powerful utility that converts HL7 FHIR structure definitions into Zod validation schemas, enabling type-safe validation of FHIR resources in TypeScript/JavaScript applications.
- Converts FHIR StructureDefinitions to Zod schemas
- Handles complex nested structures and references
- Resolves circular dependencies intelligently
- Currently supports FHIR R4 format
- Provides both CLI and programmatic API
- Generates TypeScript code with proper type annotations
# Install globally
npm install -g fhir2zod
# Or install as a dev dependency in your project
npm install --save-dev fhir2zod
# Using global installation
fhir2zod -f path/to/fhir-definitions.ndjson -o output-directory
# Using npx
npx fhir2zod -f path/to/fhir-definitions.ndjson -o output-directory
# Process multiple files
fhir2zod -f file1.ndjson -f file2.ndjson -o output-directory
# Add .js extension on import statements
fhir2zod -f file1.ndjson -f file2.ndjson -o output-directory -E
# Clone the repository
git clone https://github.com/yourusername/fhir2zod.git
cd fhir2zod
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
After generating schemas with fhir2zod:
// import path is depends on the command you executed.
import { PatientSchema } from './output/schema/Patient';
// Example FHIR Patient resource
const patientData = {
resourceType: "Patient",
id: "example",
name: [
{
use: "official",
family: "Smith",
given: ["John"]
}
],
gender: "male",
birthDate: "1974-12-25"
};
// Validate the patient data
const validationResult = PatientSchema.safeParse(patientData);
if (validationResult.success) {
console.log("Valid patient data:", validationResult.data);
} else {
console.error("Invalid patient data:", validationResult.error);
}
import { ObservationSchema } from './output/schema/Observation';
// Create a new FHIR Observation
const newObservation = {
resourceType: "Observation",
status: "final",
code: {
coding: [
{
system: "http://loinc.org",
code: "29463-7",
display: "Body Weight"
}
],
text: "Body Weight"
},
subject: {
reference: "Patient/example"
},
valueQuantity: {
value: 70.5,
unit: "kg",
system: "http://unitsofmeasure.org",
code: "kg"
}
};
// Validate the observation data
const validationResult = ObservationSchema.safeParse(newObservation);
console.log(validationResult.success
? "Valid observation"
: `Invalid observation: ${validationResult.error}`);
FHIR Profiles are constraints applied to base resources. fhir2zod generates schemas for these profiles and provides a way to look them up by URL:
import { profileMap } from './output/profileMap';
// Look up a profile schema by its canonical URL
// Usually, the profile url is in the metadata of the Resources.
const jpPatientProfileUrl = "http://jpfhir.jp/fhir/core/StructureDefinition/JP_Patient";
const JPPatientProfile = profileMap.get(jpPatientProfileUrl);
if (JPPatientProfile) {
// Now you can use the JP_Patient profile to validate data
const patientData = {
resourceType: "Patient",
meta: {
profile: [jpPatientProfileUrl]
},
identifier: [
{
system: "urn:oid:1.2.392.100495.20.3.51.11234567890",
value: "00000010"
}
],
name: [
{
extension: [
{
url: "http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation",
valueCode: "IDE"
}
],
use: "official",
text: "ๅฑฑ็ฐ ๅคช้",
family: "ๅฑฑ็ฐ",
given: ["ๅคช้"]
},
{
extension: [
{
url: "http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation",
valueCode: "SYL"
}
],
use: "official",
text: "ใคใใ ใฟใญใฆ",
family: "ใคใใ",
given: ["ใฟใญใฆ"]
}
],
gender: "male",
birthDate: "1974-12-25"
};
const validationResult = JPPatientProfile.safeParse(patientData);
if (validationResult.success) {
console.log("Valid JP Core Patient data");
} else {
console.error("Invalid JP Core Patient data:", validationResult.error);
}
}
FHIR2Zod follows these naming conventions for generated Zod schemas:
For standard FHIR resources and data types:
- PascalCase naming with
Schema
suffix - Example:
Patient
becomesPatientSchema
- Numbers are converted to words:
123-abc
becomesOneTwoThreeAbcSchema
- Hyphens trigger capitalization:
ab-cd-ef
becomesAbCdEfSchema
For FHIR profiles (constrained resources):
- Format:
{BaseType}{ProfileId}Schema
- Example: A JP Core Patient profile with ID
jp-atient
becomesPatientJpPatientSchema
- These schemas implement additional validations on top of the base resource schemas
Import generated schemas from their output location:
// Base resource schemas
import { PatientSchema } from './output/schema/Patient';
// Profile/constrained schemas can be accessed directly or via the profileMap
import { PatientJpPatientSchema } from './output/schema/JP_Patient';
import { profileMap } from './output/profileMap';
const jpPatientSchema = profileMap.get('http://jpfhir.jp/fhir/core/StructureDefinition/JP_Patient');
The conversion process follows these steps:
- Parsing - Loads FHIR definitions from NDJSON files
- Classification - Identifies StructureDefinition resources
- Dependency Analysis - Builds a dependency tree and sorts definitions
- Schema Generation - Creates Zod schemas following the dependency order
- Code Generation - Outputs TypeScript code for the schemas
FHIR resources often contain circular references. fhir2zod resolves this by:
- Pre-defining primitive types
- Using lazy evaluation with
z.lazy()
- Processing definitions in dependency order
- FHIR R4 (4.0.1) - Full support
- FHIR JP Core - Experimental support
Run tests with:
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- HL7 FHIRยฎ standard - https://hl7.org/fhir/
- Zod type validation library - https://github.com/colinhacks/zod