@reasonhealth/fhir-zod
v1.0.4
Published
Zod schemas for FHIR R4, R4B, R5
Downloads
625
Readme
@reasonhealth/fhir-zod
Zod schemas for FHIR R4, R4B, and R5. Generated from official FHIR StructureDefinition packages via @reasonhealth/fhir-ts-codegen.
Installation
npm install @reasonhealth/fhir-zod zod
# or
bun add @reasonhealth/fhir-zod zodPair with @types/fhir for ambient TypeScript types if you need them alongside runtime validation:
npm install --save-dev @types/fhirUsage
import { PatientSchema, BundleSchema } from '@reasonhealth/fhir-zod/r4'
// Parse and validate (throws ZodError on failure)
const patient = PatientSchema.parse(rawJson)
// Safe parse (no throw)
const result = PatientSchema.safeParse(rawJson)
if (result.success) {
console.log(result.data.resourceType) // 'Patient'
}
// Infer types directly from the schema
import type { z } from 'zod'
import { PatientSchema } from '@reasonhealth/fhir-zod/r4'
type Patient = z.infer<typeof PatientSchema>Available entry points
| Import | FHIR Version |
|--------|-------------|
| @reasonhealth/fhir-zod/r4 | FHIR R4 (4.0.1) |
| @reasonhealth/fhir-zod/r4b | FHIR R4B (4.3.0) |
| @reasonhealth/fhir-zod/r5 | FHIR R5 (5.0.0) |
Every schema file exports:
- A
*Schemaconstant for each FHIR type (e.g.PatientSchema,BundleSchema) - An inferred TypeScript
typefor each schema (e.g.type Patient = z.infer<typeof PatientSchema>)
Examples
Safe parse with error handling
import { ObservationSchema } from '@reasonhealth/fhir-zod/r4'
const result = ObservationSchema.safeParse(raw)
if (!result.success) {
console.error('Invalid Observation:', result.error.flatten())
} else {
console.log('Status:', result.data.status)
}Infer types from schemas
import { z } from 'zod'
import { PatientSchema, BundleSchema } from '@reasonhealth/fhir-zod/r4'
type Patient = z.infer<typeof PatientSchema>
type Bundle = z.infer<typeof BundleSchema>Using with @types/fhir
@types/fhir provides ambient namespace types (fhir4.Patient, etc.). Build a type-guard that bridges the two:
import { PatientSchema } from '@reasonhealth/fhir-zod/r4'
function isPatient(resource: fhir4.Resource): resource is fhir4.Patient {
return PatientSchema.safeParse(resource).success
}Bundle unpacking
import { BundleSchema, PatientSchema } from '@reasonhealth/fhir-zod/r4'
const bundle = BundleSchema.parse(raw)
const patients = (bundle.entry ?? [])
.map(e => e.resource)
.filter(r => r?.resourceType === 'Patient')
.map(r => PatientSchema.parse(r))Discriminated union
import { z } from 'zod'
import { PatientSchema, PractitionerSchema, OrganizationSchema } from '@reasonhealth/fhir-zod/r4'
const SubjectSchema = z.discriminatedUnion('resourceType', [
PatientSchema,
PractitionerSchema,
OrganizationSchema,
])
type Subject = z.infer<typeof SubjectSchema>Extend or restrict for profiles
import { PatientSchema } from '@reasonhealth/fhir-zod/r4'
const PatientSummarySchema = PatientSchema.pick({ id: true, name: true, birthDate: true })
const IdentifiedPatientSchema = PatientSchema.extend({
id: PatientSchema.shape.id.unwrap(), // make id required
})What's generated
Notable schema features:
- BackboneElements use
BackboneElementSchema.extend({})for clean inheritance - Choice types (
value[x]) expand to individual optional fields - Primitive shadow fields (
_birthDate) included for FHIR primitive extensions - Inline enums for required-strength bindings (
z.enum(['male', 'female', 'other', 'unknown'])) resourceTypeasz.literal('Patient')for discriminated union support- Circular references (e.g.
Questionnaire.item.item,ValueSet.compose) handled withz.lazy() - Schemas emitted in topological order to minimise forward references
Regenerating
bun run generateRequires @reasonhealth/fhir-ts-codegen to be available (installed via workspace). FHIR packages are downloaded automatically on first run and cached in .fhir-cache/.
Supported By
This project is proudly supported by Vermonster / ReasonHealth.
