Geonode logo
Geonode Team

Geonode Team

Dikemas kini: 7 Oktober 2026

Diterbitkan: 2 September 2026

Cara Membaca Berkas JSON di JavaScript

"Membaca berkas JSON dalam JavaScript" memiliki tiga arti berbeda tergantung di mana kode tersebut dijalankan, dan metode-metode yang digunakan tidak dapat diterapkan secara silang di antara ketiganya. Di Node, Anda membaca dari disk. Di peramban, Anda mengambil data melalui jaringan atau menerima berkas yang dipilih pengguna. Dan di kedua lingkungan tersebut, kini terdapat sintaks impor standar yang belum banyak digunakan oleh kebanyakan orang. Panduan ini mencakup semuanya, ditambah penanganan kesalahan yang mengubah kegagalan yang membingungkan menjadi kegagalan yang jelas.

Catatan singkat mengenai alasan perusahaan proxy ini menulis artikel ini: kami adalah Geonode, dan kesalahan JSON yang paling sering dilaporkan oleh pelanggan kami adalah pesan “Unexpected token '<'” pada data yang mereka ambil. Artinya, respons yang diterima berupa HTML — halaman kesalahan, pengalihan login, atau halaman pemblokiran — dan parser melaporkan dengan akurat bahwa data yang diterima bukanlah JSON. Sebelum mengubah kode apa pun, catat 200 karakter pertama dari apa yang Anda terima. Hampir semua hal lain dalam artikel ini mengasumsikan bahwa berkas tersebut memang berupa JSON, dan asumsi itulah yang paling sering gagal.

Di Node: Membaca dari Disk

Tiga pendekatan, dan salah satunya merupakan solusi modern.

**fs/promises

, cara standar:**

import { readFile } from "node:fs/promises";

const raw = await readFile("./data.json", "utf8");
const data = JSON.parse(raw);

Dokumentasi Node menjelaskan secara spesifik mengenai argumen encoding, dan hal ini penting: tanpa encoding, readFile

"mengembalikan sebuah promise yang terpenuhi dengan objek <Buffer>

yang berisi isi file"; dengan encoding, ia "terpenuhi dengan objek <string>

". JSON.parse

menerima Buffer dengan mengubahnya menjadi string, sehingga mengabaikan pengkodean biasanya berfungsi dan melakukan konversi tambahan tanpa alasan. Berikan "utf8"

.

Fitur ini juga mendukung AbortSignal

melalui opsi signal

, "memungkinkan Anda membatalkan operasi readFile

yang sedang berlangsung" — berguna ketika pembacaan merupakan bagian dari permintaan yang mungkin dibatalkan.

Sinkron, untuk kode saat startup:

import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("./config.json", "utf8"));

Pemblokiran (blocking) boleh dilakukan sebelum server mulai melayani. Namun, hal ini tidak boleh dilakukan di dalam penangan permintaan (request handler), karena akan menghentikan loop peristiwa (event loop) untuk setiap koneksi lainnya. Perbedaan inilah yang menjadi inti aturan tersebut.

**require

, hanya di CommonJS:**

const data = require("./data.json");

Ringkas dan memiliki dua sifat yang sering dilupakan orang. Fitur ini menyimpan dalam cache, sehingga panggilan kedua ``require`

` dengan jalur yang sama akan mengembalikan objek yang sama tanpa membaca ulang berkas — yang berarti mengedit berkas saat runtime tidak akan berpengaruh. Dan fitur ini tidak tersedia di modul ES.

Mengimpor Atribut: Cara Standar

Sintaks yang belum banyak digunakan orang, dan yang sebaiknya digunakan dalam kode baru.

import data from "./data.json" with { type: "json" };

Atau secara dinamis:

const data = await import("./data.json", { with: { type: "json" } });

MDN mencatat ini sebagai Baseline 2025, tersedia sejak April 2025 di seluruh browser terbaru, dengan runtime non-browser seperti Node dan Deno yang selaras dengan semantik browser untuk modul JSON.

Atribut ``type: "json"`

bukanlah sekadar hiasan. MDN menjelaskan bahwa atribut ini "memvalidasi bahwa modul disajikan dengan tipe MIME ``application/json

", dan bahwa jika berkas "disajikan dengan tipe media apa pun selain ``application/json

`, impor akan gagal".

Alasan keamanan ini layak dikutip karena menjelaskan mengapa atribut tersebut wajib dan bukan opsional:

Jika, karena suatu alasan (misalnya, server dibajak atau palsu), tipe media dalam respons server diatur ke text/javascript

(untuk sumber JavaScript), maka berkas tersebut akan diparsing dan dieksekusi sebagai kode. Jika berkas "JSON" tersebut sebenarnya berisi kode berbahaya, deklarasi ``import`

` akan secara tidak sengaja menjalankan kode eksternal, yang menimbulkan ancaman serius.

Catatan migrasi: usulan sebelumnya menggunakan kata kunci ``assert`

alih-alih ``with

. MDN menandai hal ini sebagai perubahan yang memengaruhi kompatibilitas mundur — implementasi yang menggunakan ``assert

"tidak lagi didukung". Jika Anda menemukan ``assert { type: "json" }

` dalam kode lama atau tutorial, hal tersebut perlu diperbarui.

Di Browser: Mengambil Data JSON

Kasus yang umum, dan yang menyembunyikan jebakan.

const res = await fetch("/data.json");
if (!res.ok) throw new Error(`HTTP ${res.status} from ${res.url}`);
const data = await res.json();

Pemeriksaan res.ok bukanlah hal opsional, dan mengabaikannya merupakan penyebab langsung dari kesalahan yang disebutkan dalam pengantar artikel ini. fetch tidak menolak status kesalahan HTTP — baik 403, 404, maupun 500 semuanya diproses secara normal. Memanggil .json() akan mencoba mengurai halaman kesalahan, dan Anda akan mendapatkan kesalahan sintaksis terkait karakter < yang tidak ada hubungannya dengan JSON Anda.

Untuk versi yang memberikan informasi berguna saat gagal:

async function fetchJson(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
  const type = res.headers.get("content-type") ?? "";
  if (!type.includes("application/json")) {
    const body = await res.text();
    throw new Error(`Expected JSON, got ${type}: ${body.slice(0, 200)}`);
  }
  return res.json();
}

Dua baris pemeriksaan mengubah kesalahan penguraian yang tidak jelas menjadi pesan yang menyebutkan status, tipe konten, dan apa yang sebenarnya diterima.

Perhatikan juga bahwa Response.json() tidak menerima fungsi reviver. Jika Anda membutuhkannya — untuk konversi tanggal atau penanganan bilangan bulat besar — gunakan res.text() diikuti dengan JSON.parse. Kami telah membahas reviver dan masalah presisi dalam panduan kami tentang JSON.parse.

Di Browser: File yang Dipilih Pengguna

Untuk file yang dipilih dari perangkat pengguna, gunakan File API.

<input type="file" id="picker" accept="application/json">
document.getElementById("picker").addEventListener("change", async e => {
  const file = e.target.files[0];
  if (!file) return;
  try {
    const data = JSON.parse(await file.text());
    console.log(data);
  } catch (err) {
    console.error(`Could not parse ${file.name}: ${err.message}`);
  }
});

File.text() mengembalikan sebuah promise yang akan diselesaikan dengan isi file berupa string, yang jauh lebih rapi dibandingkan metode lama FileReader beserta penangan peristiwa (event handlers)-nya. FileReader masih menjadi pilihan yang tepat jika Anda ingin mendapatkan peristiwa kemajuan (progress events) pada file berukuran sangat besar.

Ada dua hal yang perlu diingat. Browser tidak dapat membaca jalur lokal sembarangan — pengguna harus memilih file tersebut, dan hal ini merupakan batasan keamanan yang disengaja, bukan batasan yang dapat dielakkan. Selain itu, ekstensi .json tidak menjamin apa pun mengenai isinya, sehingga try /catch melakukan pekerjaan yang sebenarnya.

Fitur seret dan lepas (drag-and-drop) menggunakan objek File yang sama, yang diperoleh dari event.dataTransfer.files .

Penanganan Kesalahan yang Memberi Informasi

Kebiasaan yang membedakan masalah yang bisa diselesaikan dalam lima menit dengan yang memakan waktu satu jam.

function parseJson(text, source) {
  try {
    return JSON.parse(text);
  } catch (err) {
    throw new Error(
      `Failed to parse JSON from ${source}: ${err.message}. ` +
      `First 200 chars: ${text.slice(0, 200)}`
    );
  }
}

Yang penting adalah potongannya. JSON.parse Kesalahan menyebutkan posisi dan karakter; input yang sebenarnya memberi tahu Anda alasannya. Tiga tanda tangan mencakup sebagian besar kasus:

Unexpected token '<' — isinya adalah HTML. Halaman kesalahan, pengalihan login, atau daftar direktori.

Unexpected end of JSON input — inputnya kosong atau terpotong. Respons 204, file yang gagal ditulis sepenuhnya, atau permintaan yang Anda lupa tunggu.

Unexpected token '}' pada posisi yang masuk akal — JSON yang benar-benar tidak valid, seringkali berupa koma di akhir. JSON melarang hal tersebut meskipun JavaScript mengizinkannya.

Untuk pembacaan berkas, bedakan kegagalan pembacaan dari kegagalan parsing. ENOENT berarti berkas tersebut tidak ada, yang merupakan masalah berbeda dari isi yang tidak valid dan layak mendapatkan pesan kesalahan yang berbeda.

Memverifikasi Apa yang Anda Baca

Proses parsing berhasil. Hal ini hanya menunjukkan bahwa sintaksnya valid, dan sama sekali tidak memberi tahu apakah data tersebut sesuai dengan format yang diharapkan oleh kode Anda — dan kesenjangan antara kedua hal itulah yang menjadi sumber sebagian besar kegagalan dalam lingkungan produksi.

Parsing dan validasi adalah langkah yang terpisah. JSON.parse

akan dengan senang hati mengembalikan {"user": {"nmae": "Ada"}}

meskipun ada kesalahan ketik pada kunci, atau bidang price

yang berisi string "n/a"

padahal seharusnya angka. Kode Anda kemudian akan gagal di bagian hilir, beberapa fungsi jauh dari masalah sebenarnya, dengan pesan kesalahan yang hanya menyebutkan gejala, bukan penyebabnya.

Untuk hal apa pun di luar kendali Anda, validasi berdasarkan skema. Beberapa pustaka menangani hal ini dengan baik, dan polanya sama terlepas dari pustaka mana yang Anda pilih:

const Config = z.object({
  port: z.number().int().min(1).max(65535),
  host: z.string(),
  retries: z.number().int().default(3),
  features: z.array(z.string()).optional(),
});

const config = Config.parse(JSON.parse(await readFile("./config.json", "utf8")));

Kegagalan kini menyebutkan nama bidang, tipe yang diharapkan, dan apa yang ditemukan — itulah perbedaan antara perbaikan lima menit dan satu sore.

Untuk kasus sederhana, beberapa pernyataan validasi tidak memerlukan biaya apa pun:

const data = JSON.parse(raw);
if (!Array.isArray(data.items)) throw new Error("items must be an array");
if (data.items.length === 0) throw new Error("items is empty — check the source");

Pemeriksaan kedua ini lebih berharga daripada yang terlihat. Array kosong adalah JSON yang valid, dapat diparsing dengan baik, dan seringkali merupakan gejala, bukan hasil yang sah — misalnya, API yang tidak mengembalikan apa pun karena filter yang salah, atau proses pengikisan (scraping) yang berhasil pada halaman yang telah berubah.

Waspadalah terhadap angka yang tidak Anda hasilkan sendiri. Angka JSON diubah menjadi bilangan ganda (double) JavaScript, sehingga bilangan bulat di atas Number.MAX_SAFE_INTEGER

kehilangan presisi tanpa peringatan, tanpa menimbulkan kesalahan sama sekali. Pengidentifikasi sering menjadi korban: dua catatan basis data yang berbeda dapat diparsing menjadi nilai yang sama. Jika suatu bidang merupakan pengidentifikasi, bukan kuantitas, maka harus berupa string dalam JSON — dan jika Anda tidak mengontrol pihak yang menghasilkan data, argumen ``context.source`

` pada pemroses data akan memberikan digit aslinya.

Dan lakukan validasi di titik masuk, sekali saja. Memeriksa format data saat data masuk ke program Anda berarti semua proses selanjutnya dapat menganggap data tersebut benar. Melakukan validasi secara defensif di dua puluh tempat berarti ada dua puluh tempat yang harus diperbarui dan tidak ada satu titik pun di mana kontrak tersebut dituliskan.

Berkas Besar

JSON.parse

bersifat sinkron dan memerlukan seluruh dokumen berada di memori. Kedua hal tersebut menjadi masalah seiring bertambahnya ukuran berkas.

Panduan kasar. Di bawah satu megabyte, jangan dipikirkan. Antara satu hingga sepuluh, lakukan pengukuran — terutama di utas utama (main thread) di peramban, di mana proses parsing menghalangi rendering dan menyebabkan lag yang terlihat. Di atas sepuluh megabyte, atau sekitar sepersepuluh dari memori yang tersedia, lakukan hal lain.

Pindahkan ke luar utas utama. Di peramban, Web Worker melakukan parsing tanpa membekukan antarmuka. Di Node, utas pekerja melakukan hal yang sama untuk loop peristiwa.

Gunakan JSON yang dipisahkan oleh baris baru. Ini adalah solusi struktural, bukan sekadar jalan pintas. Satu dokumen JSON per baris berarti Anda memproses berkas berukuran berapa pun dengan penggunaan memori konstan, setiap baris diparsing secara independen, dan berkas yang terpotong tetap menghasilkan setiap catatan lengkap:

import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";

const rl = createInterface({ input: createReadStream("./data.jsonl") });
for await (const line of rl) {
  if (line.trim()) handle(JSON.parse(line));
}

Jika Anda mengontrol formatnya, ini adalah desain yang lebih baik untuk apa pun yang ditambahkan seiring waktu — dan alasan mengapa tugas pengumpulan yang crash meninggalkan file yang masih dapat digunakan, bukan file yang tidak dapat diparsing.

Gunakan parser streaming ketika formatnya berupa satu array besar yang tidak dapat Anda ubah. Beberapa pustaka mengeluarkan nilai saat nilai tersebut tiba, bukan dengan membangun seluruh pohon.

Atau minta lebih sedikit. Paginasi, pemilihan bidang, titik akhir yang lebih sempit. Hampir selalu merupakan jawaban yang tepat dan hampir selalu diabaikan karena memerlukan komunikasi dengan pihak yang mengelola API.

Menulis Kembali JSON

Secara singkat, ini adalah kebalikannya, karena biasanya itulah pertanyaan berikutnya.

import { writeFile } from "node:fs/promises";
await writeFile("./out.json", JSON.stringify(data, null, 2), "utf8");

Argumen ``null, 2`

` menghasilkan keluaran yang diindentasi, yang lebih penting daripada yang terlihat: berkas yang akan dibaca oleh manusia atau dibandingkan (diff) dalam sistem kontrol versi sebaiknya diformat, sedangkan berkas yang akan dikirimkan sebaiknya tidak diformat.

Tiga hal yang tidak bertahan saat diJSON.stringify

dan menyebabkan kehilangan data tanpa peringatan alih-alih menampilkan kesalahan. Nilai ``undefined`

dan fungsi dihapus sepenuhnya dari objek dan menjadi ``null

di dalam array. Objek ``Date

berubah menjadi string ISO, sehingga tidak dapat dikonversi kembali menjadi tanggal tanpa fungsi pemulih. Dan ``BigInt

` langsung memicu pengecualian — pendekatan standar adalah menserialisasikan bilangan bulat besar sebagai string.

Untuk menambahkan data, tulislah JSON yang dipisahkan oleh baris baru daripada menulis ulang sebuah array:

import { appendFile } from "node:fs/promises";
await appendFile("./log.jsonl", JSON.stringify(record) + "\n", "utf8");

Menambahkan data ke array JSON memerlukan pembacaan, penguraian, penambahan, dan penulisan ulang seluruh berkas — yang memakan sumber daya, dan yang dapat merusak berkas jika proses terhenti di tengah penulisan.

Pertanyaan Terkait

Bagaimana cara membaca berkas JSON di Node.js?

const data = JSON.parse(await readFile("./data.json", "utf8")) menggunakan node:fs/promises. Atau gunakan sintaks impor standar: import data from "./data.json" with { type: "json" }, yang merupakan Baseline per tahun 2025 dan berfungsi baik di Node maupun peramban.

Apakah JavaScript dapat membaca berkas lokal di peramban?

Tidak berdasarkan jalur. Peramban tidak dapat membuka berkas lokal sembarangan, yang merupakan batasan keamanan yang disengaja. Pengguna harus memilih berkas melalui <input type="file"> atau seret dan lepas, setelah itu File.text() akan menampilkan isinya.

Apa fungsi import ... with { type: "json" }?

Fungsi ini mengimpor berkas JSON sebagai modul sambil memvalidasi bahwa server menyajikannya dengan tipe MIME application/json. Tanpa pemeriksaan tersebut, berkas yang disajikan sebagai text/javascript akan diparsing dan dieksekusi sebagai kode — yang merupakan masalah keamanan yang ingin dicegah oleh atribut tersebut.

Mengapa saya mendapatkan pesan "Unexpected token '<'" saat membaca JSON?

Karena kontennya dimulai dengan <, yang berarti Anda menerima HTML alih-alih JSON — biasanya berupa halaman kesalahan atau pengalihan login. Dengan fetch, penyebabnya hampir selalu karena tidak adanya pemeriksaan res.ok, karena fetch tidak menolak status kesalahan HTTP.

Haruskah saya menggunakan require atau import untuk JSON?

Gunakan import ... with { type: "json" } pada kode baru, karena ini adalah standar dan berfungsi di modul ES. require hanya berlaku untuk CommonJS dan menyimpan hasilnya dalam cache, sehingga berkas yang diedit saat runtime tidak akan dibaca ulang. Keduanya tidak cocok untuk berkas yang berubah saat program berjalan — gunakan readFile untuk kasus tersebut.

Bagaimana cara membaca berkas JSON yang sangat besar?

Pindahkan proses parsing ke luar utas utama menggunakan worker, atau restrukturisasi data menjadi JSON yang dipisahkan oleh baris baru sehingga setiap baris diparsing secara independen dengan penggunaan memori konstan. Untuk array besar yang tidak berubah, gunakan parser streaming. Dan pertimbangkan apakah Anda dapat meminta data yang lebih sedikit sejak awal.

Apa perbedaan antara JSON.parse dan response.json()?

Response.json() membaca isi permintaan (fetch body) dan mem-parsing-nya dalam satu langkah, serta tidak menerima fungsi reviver. JSON.parse bekerja pada string yang sudah Anda miliki dan menerima fungsi reviver. Jika Anda memerlukan fungsi reviver — untuk tanggal atau bilangan bulat besar — gunakan res.text() diikuti dengan JSON.parse.

Bagaimana cara menangani berkas JSON yang mungkin tidak ada?

Tangani kesalahan pembacaan secara terpisah dari kesalahan penguraian. Di Node, kode kesalahan ENOENT berarti berkas tidak ditemukan, yang biasanya memerlukan nilai default daripada kegagalan — sedangkan SyntaxError berarti berkas ada tetapi isinya salah.

Kesimpulan

Metode yang digunakan bergantung pada lingkungan, dan pendekatan modern kini lebih seragam dibandingkan sebelumnya. import data from "./data.json" with { type: "json" } berfungsi di Node dan di peramban, telah menjadi Standar Dasar (Baseline) per tahun 2025, serta dilengkapi dengan pemeriksaan tipe MIME yang diterapkan karena alasan keamanan yang sesungguhnya, bukan sekadar formalitas.

Untuk berkas yang berubah saat program Anda berjalan, bacalah secara eksplisit — readFile dengan pengkodean "utf8" di Node, fetch dengan pemeriksaan res.ok di peramban, dan File.text() untuk sesuatu yang dipilih pengguna. Pemeriksaan res.ok tersebut adalah baris paling berharga dalam artikel ini, karena mengabaikannya merupakan penyebab langsung dari kesalahan JSON yang paling umum.

Ketika terjadi kegagalan, catat 200 karakter pertama dari input sebelum menyentuh kode apa pun. Kode status Unexpected token '<' berarti HTML, Unexpected end of JSON input berarti kosong atau terpotong, dan keduanya dijawab dengan melihat apa yang sebenarnya diterima daripada dengan menebak-nebak tentang parser.

Dan jika ukuran file semakin besar, solusi strukturalnya adalah menggunakan JSON yang dipisahkan oleh baris baru, bukan dengan menggunakan mesin yang lebih besar. Satu dokumen per baris mengalir dalam memori konstan, dapat ditambahkan dengan aman, dan tetap utuh meskipun penulisan terputus, dengan setiap catatan lengkap tetap utuh.