What does an empty FHIR search return?
A successful search normally returns a Bundle of type `searchset` with zero entries. That is different from a failed search, which should return an error status and typically an OperationOutcome.
Production-grade access is free with an account. · Synthetic demo · no account needed
Patient-authorized EHR integration
A practical TypeScript pattern for reading common patient resources without assuming that every FHIR server behaves identically.
Updated: 2026-08-25 · 11 min read
Read the Patient resource first, then issue patient-scoped searches for the clinical resources your app is authorized to access. Follow the server’s Bundle `next` links verbatim, retain provenance, and distinguish an empty search from a failed search.
Evidence boundary: Examples follow FHIR R4 REST and Bundle semantics and use fictional URLs and patient identifiers.
Author: FinchNode Engineering
| Product category | FHIR resources to evaluate | Important fields |
|---|---|---|
| Demographics | Patient | name, birthDate, gender, address, telecom |
| Labs and vitals | Observation, DiagnosticReport | status, category, code, value, effective date, reference range |
| Conditions | Condition | clinicalStatus, verificationStatus, code, onset |
| Medications | MedicationRequest, MedicationStatement | status, intent, medication, authoredOn |
type FhirResource = { resourceType: string; id?: string };
type Bundle = {
resourceType: 'Bundle';
type: 'searchset';
entry?: Array<{ resource?: FhirResource }>;
link?: Array<{ relation: string; url: string }>;
};
export async function readAll(url: string, token: string, maxPages = 20) {
const resources: FhirResource[] = [];
let next: string | undefined = url;
for (let page = 0; next && page < maxPages; page += 1) {
const response = await fetch(next, {
headers: { accept: 'application/fhir+json', authorization: `Bearer ${token}` },
});
if (!response.ok) throw await fhirError(response);
const bundle = (await response.json()) as Bundle;
if (bundle.resourceType !== 'Bundle') throw new Error('unexpected_fhir_response');
resources.push(...(bundle.entry ?? []).flatMap((item) => item.resource ? [item.resource] : []));
next = bundle.link?.find((link) => link.relation === 'next')?.url;
}
return resources;
}
Search support is advertised by a server’s CapabilityStatement and implementation guide. Treat these URLs as patterns, not a promise that every source accepts every parameter.
GET {fhirBase}/Patient/{patientId}
GET {fhirBase}/Observation?patient={patientId}&category=laboratory&_count=100
GET {fhirBase}/Condition?patient={patientId}&_count=100
GET {fhirBase}/MedicationRequest?patient={patientId}&_count=100
Accept: application/fhir+json
Authorization: Bearer {accessToken}
A successful search normally returns a Bundle of type `searchset` with zero entries. That is different from a failed search, which should return an error status and typically an OperationOutcome.
Follow the Bundle link whose relation is `next`. Do not infer or rewrite the continuation URL because servers can use opaque paging state.
Individual laboratory results are commonly represented as Observation resources, often with related DiagnosticReport resources. The exact profiles and search support depend on the server.
Some servers support Patient `$everything`, but its content, parameters, size, and paging behavior vary. Resource-specific searches are often easier to operate and troubleshoot.