Chaining, _include, _revinclude; paging dan Bundle searchset
Filter lewat referensi, sertakan resource yang dirujuk, lalu ikuti halaman hasilnya
_include/_revinclude, entry.search.mode, dan paging lewat link next.
Sebelumnya, di lesson 0008, Anda menyusun query search: tipe parameter, modifier, prefix, AND/OR. Lesson ini menjawab dua pertanyaan berikutnya: bagaimana membawa resource yang dirujuk dalam request yang sama, dan bagaimana membaca hasil yang dibagi per halaman.
1. Chaining, _include, dan _revinclude
Dua hal yang sering tertukar: chaining memfilter lewat referensi, _include menambah resource yang dirujuk ke hasil.
| Contoh resmi | Artinya | |
|---|---|---|
| Chaining | DiagnosticReport?subject:Patient.name=peter | "all the lab reports that have a subject which is a patient, whose name includes "peter"". Hasilnya tetap DiagnosticReport saja. |
_include | MedicationRequest?_include=MedicationRequest:patient | sertakan "any patient that the medication prescriptions in the result set refer to" (arah maju: hasil → yang dirujuk) |
_revinclude | MedicationRequest?_revinclude=Provenance:target | sertakan "all the provenance resources that refer to them" (arah balik: yang merujuk → hasil) |
Semua: [2]. Nilai _include/_revinclude berbentuk [tipe sumber]:[search parameter] (opsional :[tipe target]); include berantai hanya terjadi dengan modifier :iterate. Chaining terbalik (_has) ada, tapi dukungannya "limited".
Jawaban untuk pertanyaan target, dari dua arah:
// Dari Observation: hasil = Observation, Patient ikut sebagai include
GET [base]/Observation?patient=123&code=http://loinc.org|29463-7&_include=Observation:patient
// Dari Patient: hasil = Patient 123, Observation yang merujuknya ikut sebagai include
GET [base]/Patient?_id=123&_revinclude=Observation:patient
Padanan JS: _include mirip include di Prisma (prisma.observation.findMany({ where: …, include: { patient: true } })), tapi hasilnya tidak bersarang:
Patient muncul sebagai entry terpisah di Bundle (bagian 2), dan bisa muncul lagi di halaman berikutnya bila dirujuk Observation di halaman itu. [2] Kode LOINC 29463-7 (Body Weight) diambil dari contoh Observation resmi R4.
[4]
Latihan 1 — Filter atau sertakan?
2. Paging dan Bundle searchset
Hasil search selalu Bundle dengan type: "searchset". [1] Bentuk ringkasnya:
{
"resourceType": "Bundle",
"type": "searchset",
"total": 5000, // jumlah match di SEMUA halaman (include tidak dihitung)
"link": [
{ "relation": "self", "url": "https://fhir.example.org/Observation?patient=123&_include=Observation:patient&_count=50" },
{ "relation": "next", "url": "https://fhir.example.org/Observation?_getpages=a1b2c3&_offset=50" }
],
"entry": [
{ "fullUrl": "https://fhir.example.org/Observation/obs-1",
"resource": { "resourceType": "Observation", "id": "obs-1", "...": "..." },
"search": { "mode": "match" } },
{ "fullUrl": "https://fhir.example.org/Patient/123",
"resource": { "resourceType": "Patient", "id": "123", "...": "..." },
"search": { "mode": "include" } }
]
}
Bundle.total: "the total number of entries of type 'match' across all pages in the search".entry.search.mode:match | include | outcome. [3] Server "SHALL populate the entry.search.mode element ... so that the client can pick apart matches and includes". [2]_count= berapa resource per halaman (permintaan, bukan janji)._summary=count= hanya jumlah, tanpa resource._sort= urutan. [2]- Link halaman (
first,previous,next,last) "are opaque to the client, have no dictated structure, and only the server understands them"; "The client must use the server supplied links in order to traverse the pages." [1] Format URLnextdi contoh atas hanya ilustrasi.
// Node 18+ / browser: ikuti link "next" sampai habis, tanpa menyusun URL halaman sendiri
async function* searchAll(url: string): AsyncGenerator<any> {
let next: string | undefined = url;
while (next) {
const res = await fetch(next, { headers: { Accept: "application/fhir+json" } });
if (!res.ok) throw new Error(JSON.stringify(await res.json())); // body = OperationOutcome (lesson 0011)
const bundle = await res.json();
for (const entry of bundle.entry ?? []) yield entry; // cek entry.search.mode
next = bundle.link?.find((l: any) => l.relation === "next")?.url;
}
}
Untuk jutaan resource, paging search bukan alat yang tepat; itu tugas Bulk Data $export di lesson 0013.
Latihan 2 — Membaca Bundle hasil search
Latihan 3 — Ucapkan dengan suara keras (±5 menit)
Pasang timer dan jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.
"Bagaimana Anda mengambil semua Observation berat badan milik satu pasien, beserta Patient-nya, dalam satu request? Bagaimana kalau hasilnya ribuan?" (90 detik)
Bandingkan dengan jawaban contoh dan checklist-nya
"Saya search Observation: GET [base]/Observation?patient=123&code=http://loinc.org|29463-7&_include=Observation:patient&_count=50.
code itu token, jadi saya tulis system|code supaya tidak tertukar dengan sistem kode lain; kalau perlu rentang waktu, tambah
date=ge2024-01-01&date=le2024-12-31, parameter yang diulang berarti AND. _include membuat server ikut mengirim Patient yang dirujuk,
dan di Bundle searchset saya bedakan lewat entry.search.mode: match untuk Observation, include untuk Patient.
Alternatifnya dari sisi Patient: Patient?_id=123&_revinclude=Observation:patient. Kalau ribuan, server mem-page hasilnya; saya ikuti
Bundle.link dengan relation next sampai habis, tidak menyusun URL halaman sendiri karena link itu opaque. Jumlah total ada di
Bundle.total, yang hanya menghitung match. Di Prisma, padanannya findMany dengan include plus cursor pagination."
Checklist: apakah Anda menyebut…
- search Observation dengan
patient=123(atausubject=Patient/123)? codesebagai tokenhttp://loinc.org|29463-7(system|code)?_include=Observation:patient, atau_revincludedari sisi Patient?- hasil Bundle
searchset;entry.search.modemembedakan match dan include? - paging:
_countdan mengikuti linknextyang opaque, bukan menyusun URL sendiri? - filter tanggal dengan prefix
ge/le, parameter diulang = AND, koma = OR?
Lulus kalau minimal 4 dari 6.
Ringkasan untuk dicetak: Reference: REST, search, Bundle · Istilah: GLOSSARY.md (bagian "REST API")
Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "bagaimana _include=...:iterate bekerja?"
Sumber (semua FHIR R4 4.0.1, diakses 2026-10-26): [1] RESTful API · [2] Search · [3] Bundle · [4] Observation example (Body Weight)