Package Exports
- @fhir-dsl/core
Readme
@fhir-dsl/core
A fully type-safe FHIR query builder for TypeScript, inspired by Kysely.
Construct FHIR REST queries with compile-time safety for resource types, search parameters, includes, and profiles. No more guessing parameter names or hoping your query string is valid.
Install
npm install @fhir-dsl/core @fhir-dsl/typesSetup
1. Generate your FHIR types
Before using the query builder, generate typed definitions for your FHIR version and profiles using @fhir-dsl/cli:
npx @fhir-dsl/cli generate \
--version r4 \
--ig hl7.fhir.us.core@6.1.0 \
--out ./src/fhirThis generates a FhirSchema type along with typed resources, search parameters, profiles, and a pre-configured client — everything the query builder needs for compile-time safety.
2. Create a client
import { createFhirClient } from "@fhir-dsl/core";
import type { FhirSchema } from "./fhir"; // generated in step 1
const fhir = createFhirClient<FhirSchema>({
baseUrl: "https://hapi.fhir.org/baseR4",
auth: {
type: "bearer",
credentials: "your-token",
},
});Usage
Search resources
const result = await fhir
.search("Patient")
.where("family", "eq", "Smith")
.where("birthdate", "ge", "1990-01-01")
.sort("birthdate", "desc")
.count(10)
.execute();
// result.data - Patient[] (typed)
// result.total - total count from server
// result.included - included resources (typed)
// result.link - pagination links
// result.raw - raw Bundle responseEvery method is fully typed. where("family", ...) only accepts valid search parameters for Patient, and the operator is constrained to what makes sense for that parameter type (string, token, date, etc.).
Include related resources
const result = await fhir
.search("Patient")
.where("name", "eq", "Smith")
.include("general-practitioner")
.execute();
// result.data - Patient[]
// result.included - Practitioner[] (inferred from include target)The include parameter is typed to only accept valid include paths for the resource, and the included resource type is automatically inferred.
Narrow returned fields
Use .select() to request only specific top-level fields. The result type narrows to match, and the query compiles to FHIR's _elements search parameter:
const result = await fhir
.search("Patient")
.where("family", "eq", "Smith")
.select(["id", "name", "birthDate"])
.execute();
// result.data[0]: { resourceType: "Patient"; id?: string; name?: HumanName[]; birthDate?: FhirDate }Only top-level element names are accepted (matches the _elements spec). resourceType is always preserved. Calling .select() twice replaces the earlier selection.
Profile-aware queries
const vitals = await fhir
.search("Observation", "http://hl7.org/fhir/us/core/StructureDefinition/us-core-vital-signs")
.where("patient", "eq", "Patient/123")
.where("status", "eq", "final")
.execute();
// vitals.data is typed as USCoreVitalSignsProfile[]When you pass a profile URL as the second argument, the result type narrows to that profile's interface.
Read a single resource
const patient = await fhir.read("Patient", "123").execute();
// patient is typed as PatientTransactions
const result = await fhir
.transaction()
.create({
resourceType: "Patient",
name: [{ family: "Doe", given: ["Jane"] }],
})
.update({
resourceType: "Patient",
id: "456",
name: [{ family: "Smith" }],
})
.delete("Observation", "789")
.execute();Batch operations
const result = await fhir
.batch()
.create({
resourceType: "Patient",
name: [{ family: "Doe", given: ["Jane"] }],
})
.delete("Observation", "789")
.execute();Compile without executing
Every builder has a compile() method that returns the raw query representation without hitting the server:
const query = fhir
.search("Observation")
.where("status", "eq", "final")
.count(50)
.compile();
// {
// method: "GET",
// path: "Observation",
// params: [
// { name: "status", value: "final" },
// { name: "_count", value: 50 }
// ]
// }Design
- Immutable builders - Every method returns a new builder instance. Safe to store, fork, and compose.
- Schema-driven - All type safety comes from the
FhirSchemageneric, which is generated from actual FHIR StructureDefinitions. - No runtime magic - The builder compiles to plain objects. Bring your own HTTP client or use the built-in fetch executor.
Search Parameter Operators
| Param Type | Operators |
|---|---|
string |
eq, contains, exact |
token |
eq, not, in, not-in, text, above, below, of-type |
date |
eq, ne, gt, ge, lt, le, sa, eb, ap |
number |
eq, ne, gt, ge, lt, le |
quantity |
eq, ne, gt, ge, lt, le, sa, eb, ap |
reference |
eq |
uri |
eq, above, below |