Hampir semua aplikasi web perlu bertukar data dengan server, dan fetch adalah fungsi bawaan browser sekaligus Node.js untuk pekerjaan itu, yaitu mengirim permintaan HTTP dengan gaya berbasis promise. Topik ini mempraktikkan permintaan GET beserta pembacaan JSON-nya, pemeriksaan status HTTP, permintaan POST untuk mengirim data, dan pembedaan error jaringan dari respons yang gagal. Semua contoh dieksekusi terhadap server kecil yang dinyalakan oleh skrip contohnya sendiri, sehingga keluarannya dapat direproduksi apa adanya.
Permintaan GET dan pembacaan JSON#
fetch(url) mengembalikan promise yang terpenuhi dengan objek Response begitu respons server tiba. Objek itu membawa status, header respons, dan badan respons yang belum terbaca, sehingga butuh satu langkah lagi untuk mengambil isinya. Untuk respons JSON, langkah itu adalah res.json(), yang membaca badan respons lalu mengurainya menjadi nilai JavaScript.
// Server lokal kecil sebagai target, lalu permintaan GET dengan fetch.
const http = require("node:http");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ judul: "Belanja mingguan", isi: "kopi, beras, telur" }));
});
server.listen(0, "127.0.0.1", async () => {
const port = server.address().port;
const res = await fetch(`http://127.0.0.1:${port}/api/catatan`);
const data = await res.json();
console.log("status:", res.status);
console.log("res.ok:", res.ok);
console.log("judul:", data.judul);
console.log("isi:", data.isi);
server.close();
});status: 200
res.ok: true
judul: Belanja mingguan
isi: kopi, beras, telurPanggilan fetch ditunggu lewat await sebelum badannya dibaca, dan res.json() pun ditunggu dengan await karena proses penguraiannya juga memakan waktu. Server targetnya adalah server sementara yang dibuat modul node:http, dinyalakan pada port bebas yang dipilih sistem lewat argumen 0, lalu dimatikan lagi di akhir. Pola ini dipakai agar contoh tetap mandiri, sedangkan pada aplikasi nyata URL-nya menunjuk ke server sungguhan di internet.
Objek Response tidak hanya membawa badan respons. res.status menyimpan kode statusnya, res.ok merangkum keberhasilannya, dan header responsnya dibaca lewat res.headers.get(), misalnya res.headers.get("content-type") untuk memastikan bentuk isinya sebelum diurai. Pembacaan badan juga tersedia dalam bentuk lain selain res.json(), yaitu res.text() untuk teks biasa dan res.blob() untuk data biner seperti gambar. Pemilihan bentuknya mengikuti isi respons, dan untuk API yang membalas JSON, res.json() adalah pilihan utamanya.
Memeriksa status HTTP dengan res.ok#
Respons yang tiba dari server tidak selalu membawa data yang diminta, sehingga perlu diperiksa lebih dahulu. Properti res.status berisi kode status HTTP, sedangkan res.ok bernilai true hanya untuk rentang 200 sampai 299. Program berikut meminta catatan yang memang tidak ada, sehingga server membalas 404.
// Status 4xx tidak melempar error, res.ok hanya bernilai false.
const http = require("node:http");
const server = http.createServer((req, res) => {
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ pesan: "catatan tidak ditemukan" }));
});
server.listen(0, "127.0.0.1", async () => {
const port = server.address().port;
const res = await fetch(`http://127.0.0.1:${port}/api/catatan/99`);
console.log("status:", res.status);
console.log("res.ok:", res.ok);
if (!res.ok) {
const data = await res.json();
console.log("pesan server:", data.pesan);
}
server.close();
});status: 404
res.ok: false
pesan server: catatan tidak ditemukanRespons 404 tetap diurai seperti biasa, dan pengecekannya dilakukan sebelum data dipakai. Banyak server menyertakan pesan penjelasan pada badan respons gagalnya, sehingga membaca res.json() di dalam cabang !res.ok tetap berguna untuk menampilkannya.
Mengirim data dengan POST#
Permintaan GET hanya membaca, sedangkan mengirim data membutuhkan konfigurasi tambahan pada opsi kedua fetch. method menyatakan metode HTTP-nya, headers menyatakan bentuk badan yang dikirim, dan body berisi datanya yang diserialisasi dengan JSON.stringify. Server pada contoh membalas dengan status 201, kode yang berarti sebuah data baru berhasil dibuat.
// Mengirim data JSON dengan POST, server membalas dengan status 201.
const http = require("node:http");
const server = http.createServer((req, res) => {
let tubuh = "";
req.on("data", (potongan) => {
tubuh += potongan;
});
req.on("end", () => {
const catatan = JSON.parse(tubuh);
res.writeHead(201, { "Content-Type": "application/json" });
res.end(JSON.stringify({ pesan: "catatan tersimpan", judul: catatan.judul }));
});
});
server.listen(0, "127.0.0.1", async () => {
const port = server.address().port;
const res = await fetch(`http://127.0.0.1:${port}/api/catatan`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ judul: "Daftar tugas", isi: "belajar fetch" }),
});
const data = await res.json();
console.log("status:", res.status);
console.log("pesan:", data.pesan);
console.log("judul:", data.judul);
server.close();
});status: 201
pesan: catatan tersimpan
judul: Daftar tugasHeader Content-Type: application/json menyatakan bentuk data yang dikirim, dan servernya memakai informasi itu saat mengurai badan permintaan. Tanpa header itu, banyak server menolak badan JSON atau salah menafsirkannya, sehingga JSON.stringify pada body dan Content-Type: application/json pada headers sebaiknya selalu ditulis bersama. Untuk bentuk data lain, misalnya unggahan berkas, header dan badannya memang berbeda, tetapi pola opsi kedua fetch ini tetap yang sama.
Server sementara di contoh ini mengumpulkan badan permintaan potongan demi potongan, lalu menguraikannya dengan JSON.parse setelah data selesai diterima. Balasannya membuktikan data sampai utuh, karena judul yang dikirim ikut tercetak pada respons. Pada aplikasi nyata, pola POST seperti ini dipakai untuk membuat data baru, misalnya menambah catatan ke daftar milik pengguna.
Kegagalan jaringan dan contoh API nyata#
Jalur kegagalan yang benar-benar membuat fetch menolak adalah kegagalan jaringan, yaitu ketika permintaan tidak pernah sampai atau tidak pernah dibalas. Penolakannya berupa TypeError dengan pesan singkat, sedangkan detail penyebabnya tersimpan pada properti cause. Penanganannya memakai try/catch di sekeliling await.
// Kegagalan jaringan melempar TypeError, berbeda dari status HTTP 4xx dan 5xx.
async function utama() {
try {
await fetch("http://127.0.0.1:41234/api/catatan");
} catch (err) {
console.log("jenis error:", err.name);
console.log("pesan:", err.message);
console.log("sebab:", err.cause.code);
}
}
utama();jenis error: TypeError
pesan: fetch failed
sebab: ECONNREFUSEDPort 41234 pada contoh sengaja dibiarkan kosong sehingga koneksi ditolak segera, dan kode penyebabnya ECONNREFUSED muncul pada keluaran. Perbedaannya dengan kasus 404 di atas penting untuk dipegang. Status HTTP berarti server menjawab, sedangkan error jaringan berarti percakapan tidak pernah terjadi. Contoh berikut adalah bentuk permintaan ke API publik yang keluarannya bergantung pada isi server saat itu, sehingga tidak dieksekusi di sandbox.
// tidak-dijalankan
// Meminta satu data dari API publik, keluarannya bergantung pada server.
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => {
console.log("status:", res.status);
return res.json();
})
.then((data) => console.log("judul tugas:", data.title));Bentuknya memakai rantai then tanpa await, dan polanya sama persis dengan contoh-contoh lokal di atas, yaitu periksa status, baca badan respons, lalu olah datanya.
Kegagalan jaringan juga mencakup permintaan yang menggantung terlalu lama, sedangkan fetch tidak menyediakan opsi batas waktu yang seragam di semua lingkungan. Cara yang lazim adalah melombakannya dengan timer penolak memakai Promise.race, sehingga permintaan yang tidak kunjung dibalas kalah oleh kegagalan buatan sendiri. Pola itu meminjam kombinator dari topik Kombinator Promise, dan kombinasi fetch dengan timer cukup untuk sebagian besar kebutuhan sehari-hari.
Ringkasan#
fetchmengembalikan promise yang terpenuhi dengan objek Response begitu respons server tiba.- Badan respons dibaca lewat
res.json()yang juga berupa promise, dan hanya dapat dibaca sekali. - Status HTTP diperiksa lewat
res.statusdanres.ok, karena 4xx dan 5xx tidak membuatfetchmenolak. - Error jaringan melempar TypeError yang ditangkap
try/catch, berbeda jalurnya dari respons gagal milik server.
Referensi#
- Using Fetch — MDN Web Docs — panduan resmi permintaan fetch, dari GET sampai POST beserta penanganannya.
- Response — MDN Web Docs — dokumentasi properti Response seperti status, ok, dan metode json.
- Fetch — The Modern JavaScript Tutorial — bab yang merinci opsi permintaan dan pembacaan respons.