npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fhir-dsl/core

v1.2.2

Published

Type-safe FHIR query builder inspired by Kysely

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/types

Setup

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 [email protected] \
  --out ./src/fhir

This 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

The CLI emits a pre-bound createClient for your generated schema — that's the recommended entry point. It calls createFhirClient<FhirSchema> under the hood with the schema, search params, includes, and validators all pre-wired:

import { createClient } from "./fhir/r4"; // generated in step 1

const fhir = createClient({
  baseUrl: "https://hapi.fhir.org/baseR4",
  auth: { type: "bearer", credentials: "your-token" },
});

If you prefer to wire the schema yourself (e.g. when bundling for a web client), createFhirClient is exported directly:

import { createFhirClient } from "@fhir-dsl/core";
import type { FhirSchema } from "./fhir";

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 response

Every 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 Patient

Transactions

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();

Server-side _filter via FHIRPath

.filter() accepts a raw FHIRPath string or a built FhirPathExpr (anything with .compile(): string) and emits the FHIR _filter search param. Compose typed expressions with @fhir-dsl/fhirpath for end-to-end type safety:

import { fhirpath } from "@fhir-dsl/fhirpath";

const officialFamilyIsSmith = fhirpath<"Patient">("Patient")
  .name.where(($) => $.use.eq("official"))
  .family.eq("Smith");

const result = await fhir
  .search("Patient")
  .filter(officialFamilyIsSmith)
  .execute();
// GET /Patient?_filter=Patient.name.where(use%20%3D%20%27official%27).family%20%3D%20%27Smith%27

Not every server supports _filter. Check CapabilityStatement.rest.searchParam before relying on it. See the FHIRPath + Query Builder guide for the full pattern catalogue (server _filter, post-fetch projection, client filtering, aggregates).

Errors

Every error from this package extends FhirDslError — pattern-match on kind, read structured context, and serialise with toJSON():

| Class | kind | When | |---|---|---| | FhirRequestError | core.request | Non-2xx HTTP response (context.status, context.statusText, context.operationOutcome) | | AsyncPollingTimeoutError | core.async_polling_timeout | async polling exceeded maxAttempts | | ValidationError | core.validation | A resource failed Standard Schema validation; context.issues lists every failure | | ValidationUnavailableError | core.validation_unavailable | .validate() called without a SchemaRegistry |

import { isFhirDslError } from "@fhir-dsl/utils";
import { FhirRequestError } from "@fhir-dsl/core";

try {
  await fhir.read("Patient", "missing").execute();
} catch (err) {
  if (err instanceof FhirRequestError) {
    console.error(err.context.status, err.context.operationOutcome);
  } else if (isFhirDslError(err)) {
    console.error(err.kind, err.context);
  } else {
    throw err;
  }
}

Or with the Result toolkit, no try/catch:

import { tryAsync } from "@fhir-dsl/utils";
const r = await tryAsync(() => fhir.read("Patient", "missing").execute());
if (!r.ok) console.error(r.error.kind);

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 FhirSchema generic, 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 |

License

MIT