Lesson 0014 · FHIR · Modul Bulk Data · ±21 menit

Manifest, NDJSON, _type/_since; batas JSON.parse di Node

Apa yang Anda terima setelah export selesai, dan kenapa file itu tidak boleh dibaca sekaligus

Sebelumnya, di lesson 0013, polling URL status berakhir dengan 200 OK + manifest. Lesson ini membedah manifest itu, format file NDJSON yang ditunjuknya, parameter _type dan _since, lalu masalah pertama di sisi Node: JSON.parse satu file besar. Seperti seluruh modul ini, tanpa lab: potongan kode hanya untuk dibaca dan dijelaskan.

Target lesson ini: Anda bisa membaca manifest Bulk Data (transactionTime, output, error, requiresAccessToken), menjelaskan NDJSON dan _since untuk export inkremental, dan menyebut tiga alasan JSON.parse satu file besar itu salah: batas string, event loop terblok, dan format NDJSON.

1. Manifest, NDJSON, _type dan _since

{
  "transactionTime": "2026-10-26T01:00:00Z",
  "request": "https://fhir.example.org/fhir/Patient/$export?_type=Patient,Observation",
  "requiresAccessToken": true,
  "output": [
    { "type": "Patient",     "url": "https://files.example.org/42/Patient_1.ndjson" },
    { "type": "Observation", "url": "https://files.example.org/42/Observation_1.ndjson", "count": 1000000 },
    { "type": "Observation", "url": "https://files.example.org/42/Observation_2.ndjson", "count": 734120 }
  ],
  "deleted": [],
  "error": [
    { "type": "OperationOutcome", "url": "https://files.example.org/42/err_1.ndjson" }
  ]
}
FieldArti (Bulk Data IG v2.0.0)
transactionTimewajib; "the server's time when the query is run". Export SHALL memuat semua resource yang cocok sampai dan termasuk instant ini.
requiresAccessTokenwajib; true berarti unduhan file butuh token yang sama. Boleh false untuk file server dengan skema lain, mis. URL bucket S3.
outputwajib; satu item {type, url, count?} per file. "Each file SHALL contain resources of only one type, but a server MAY create more than one file for each resource type returned."
errorwajib (boleh kosong); file NDJSON berisi OperationOutcome. Export yang sebagian gagal tetap 200: "In the case of a partial success, the server SHALL use a 200 status code".
deletedopsional; dengan _since, berisi Bundle transaction dengan request.method = DELETE untuk resource yang dihapus sejak waktu itu.
header ExpiresSHOULD; kapan file tidak lagi tersedia. Unduh dan proses sebelum itu.

Kutipan tabel: [1]

NDJSON (newline delimited JSON): "resources are serialized with no whitespace, and separated by a newline pair (characters 13 and 10)", MIME type application/fhir+ndjson, dan "each nd-json document contains only resources of a single type". [2] Padanan JS: format ini sama dengan JSON Lines yang mungkin Anda kenal dari log. Isi Observation_1.ndjson, dua baris pertama:

{"resourceType":"Observation","id":"obs-1","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"8310-5","display":"Body temperature"}]},"subject":{"reference":"Patient/p-1"},"valueQuantity":{"value":36.50,"unit":"Cel","system":"http://unitsofmeasure.org","code":"Cel"}}
{"resourceType":"Observation","id":"obs-2","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"8310-5","display":"Body temperature"}]},"subject":{"reference":"Patient/p-2"},"valueQuantity":{"value":37.20,"unit":"Cel","system":"http://unitsofmeasure.org","code":"Cel"}}

Kode LOINC 8310-5 (Body temperature) dari profil vital signs R4. [3] Perhatikan 36.50; kita kembali ke angka ini di lesson 0016.

Parameter kick-off yang paling sering ditanya: [1]

Data yang diekspor hanya versi terbaru setiap resource: "The exported data SHALL include only the most recent version of any exported resources". [1]

Latihan 1 — Membaca manifest

Manifest menunjuk file NDJSON yang bisa berukuran gigabyte. Langkah berikutnya membaca file itu di Node, dan cara yang paling jelas justru yang salah.

2. Di Node: kenapa JSON.parse satu file besar itu salah

// JANGAN untuk file hasil $export
const text = await readFile("Observation_1.ndjson", "utf8");   // seluruh file → satu string
const all = JSON.parse(text);                                   // + seluruh objek di heap, sekaligus
MasalahKenapa
Batas stringbuffer.constants.MAX_STRING_LENGTH: "The largest length allowed for a single string instance", dan "This value may depend on the JS engine". [4] Di Node 24.21.0 yang saya cek nilainya 536.870.888 UTF-16 code unit, jauh di bawah file 3 GB.
MemoriTeks mentah dan hasil parse ada di heap bersamaan; jutaan objek kecil ditahan sampai Anda selesai.
Event loop terblokJSON.parse dan JSON.stringify "are O(n) in the length of the input, for large n they can take surprisingly long". Contoh resmi: parse string 50 MB makan 1,3 detik, dan selama itu tidak ada request lain yang dilayani. [5]
FormatNDJSON adalah banyak dokumen JSON dipisah newline, bukan satu array; JSON.parse pada seluruh isi file langsung melempar SyntaxError.

Solusinya selalu sama: jangan pernah memegang seluruh file. Baca potongan demi potongan, parse satu resource setiap kali, lalu buang.

Latihan 2 — Batas JSON.parse

Latihan 3 — Ucapkan dengan suara keras

Pasang timer dan jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.

"Export Anda selesai dengan 200 OK. Jelaskan isi manifest-nya dan bagaimana Anda memakainya untuk export harian yang inkremental." (60 detik)
Bandingkan dengan jawaban contoh dan checklist-nya

"Body 200 adalah manifest JSON. transactionTime adalah jam server saat query dijalankan; export memuat semua resource yang cocok sampai instant itu. output berisi satu item {type, url, count?} per file: satu file hanya satu tipe resource, tapi satu tipe bisa dipecah ke beberapa file. error berisi file NDJSON OperationOutcome; export yang sebagian gagal tetap 200. requiresAccessToken: true berarti unduhan file butuh token yang sama. Setiap file NDJSON, satu baris satu resource, dengan MIME type application/fhir+ndjson, dan harus diunduh sebelum header Expires. Untuk export harian, saya simpan transactionTime dan memakainya sebagai _since berikutnya, bukan jam client; _type membatasi tipe resource, dan dengan _since array deleted bisa berisi resource yang dihapus."

Checklist: apakah Anda menyebut…

  • transactionTime: jam server saat query dijalankan, export memuat sampai instant itu?
  • output: {type, url, count?} per file; satu file satu tipe, satu tipe bisa beberapa file?
  • error: file NDJSON berisi OperationOutcome; sukses sebagian tetap 200?
  • requiresAccessToken: true: unduhan file memakai token yang sama?
  • NDJSON satu baris satu resource (application/fhir+ndjson), diunduh sebelum Expires?
  • _since = transactionTime sebelumnya (bukan jam client), _type membatasi tipe, deleted untuk yang dihapus?

Lulus jika minimal 4 dari 6.

Berikutnya, lesson 0015: cara yang benar, yaitu streaming baris per baris, batching ke database, backpressure, dan pipeline.

Istilah: GLOSSARY.md (bagian "Bulk Data dan JSON skala besar") · Indeks: semua lesson FHIR

Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "apa beda Patient/$export dan Group/[id]/$export untuk use case analitik?", "bagaimana menulis hasil export ke S3 dengan backpressure yang benar?", atau "kapan lebih baik memakai worker thread daripada streaming?"

Sumber: [1] Bulk Data Access IG v2.0.0 (STU 2): Export · [2] FHIR R4: ND-JSON Representation of Resources · [3] FHIR R4: Vital Signs profile · [4] Node.js: buffer.constants.MAX_STRING_LENGTH · [5] Node.js Learn: Don't Block the Event Loop