Dua pemula pernah mengirim permintaan POST yang persis sama ke server tiruan yang sama, tetapi hasilnya bertolak belakang: milik Siti diproses menjadi data baru, milik Budi dijawab 400 Bad Request dengan pesan “body tidak dapat diurai”. Bedanya satu header: Content-Type. Server tidak bisa menebak format isi pesan — ia membaca labelnya, dan bila label salah, isi sebaik apa pun jadi tidak terbaca. Chapter ini merapikan pengelolaan header: dari Content-Type dan Accept, header kustom, sampai membaca header respons lewat object Headers.
Berkas contoh kembali menanam server tiruannya sendiri; server mencetak header yang diterima sehingga kita bisa membuktikan apa yang benar-benar dikirim fetch. Jalankan node kelola-header.mjs untuk mereproduksi keluarannya.
Content-Type: Label Format Isi Pesan #
Header Content-Type menjawab satu pertanyaan: “isi pesan ini dalam format apa?”. Untuk API JSON, nilainya application/json — sering dilengkapi charset=utf-8 pada arah respons. Aturan praktisnya:
- Body JSON dikirim → sertakan
Content-Type: application/jsondi request. - Tanpa header itu, banyak server menolak body atau mengurainya sebagai teks mentah.
- Pada response,
Content-Typemenjadi dasar keputusan client memilihresponse.json()atauresponse.text().
Perhatikan bahwa Content-Type tidak mengubah data — ia hanya deskripsi. Data JSON tetap diserialisasi JSON.stringify seperti biasa; header-lah yang memberi tahu server cara membaca teksnya.
Mengirim Header lewat Options #
Semua header request dikirim lewat properti headers pada options fetch. Berkas berikut mengirim tiga header — format isi, format yang diharapkan, dan satu header kustom — lalu server tiruan mencetak apa yang diterimanya, dan client membaca header respons:
// kelola-header.mjs
// Mengirim header permintaan dan membaca header respons.
// Jalankan di terminal: node kelola-header.mjs (tanpa internet).
import http from "node:http";
const server = http.createServer((req, res) => {
// Server memeriksa header yang dikirim client.
console.log("SERVER content-type :", req.headers["content-type"] ?? "(tidak ada)");
console.log("SERVER accept :", req.headers["accept"] ?? "(tidak ada)");
console.log("SERVER x-api-key :", req.headers["x-api-key"] ?? "(tidak ada)");
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
"X-Server-Waktu": "server-tiruan-v1",
"Cache-Control": "max-age=60",
});
res.end(JSON.stringify({ id: 17, judul: "Kamus Bahasa" }));
});
server.listen(0, "127.0.0.1", async () => {
const port = server.address().port;
const response = await fetch(`http://127.0.0.1:${port}/produk/17`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-Api-Key": "kunci-demo-budi-123",
},
});
// response.headers adalah object Headers: baca satuan atau iterasi.
console.log("CLIENT content-type :", response.headers.get("Content-Type"));
console.log("CLIENT cache-control:", response.headers.get("Cache-Control"));
console.log("CLIENT ada x-server-waktu?", response.headers.has("x-server-waktu"));
console.log("--- semua header respons (urut abjad):");
for (const [nama, nilai] of response.headers) {
if (nama === "date") continue; // header date berubah setiap saat, dilewati agar keluaran stabil
console.log(`${nama}: ${nilai}`);
}
server.closeAllConnections();
server.close();
});Output yang Dihasilkan:
SERVER content-type : application/json
SERVER accept : application/json
SERVER x-api-key : kunci-demo-budi-123
CLIENT content-type : application/json; charset=utf-8
CLIENT cache-control: max-age=60
CLIENT ada x-server-waktu? true
--- semua header respons (urut abjad):
cache-control: max-age=60
connection: keep-alive
content-type: application/json; charset=utf-8
keep-alive: timeout=5
transfer-encoding: chunked
x-server-waktu: server-tiruan-v1Mari kita telaah baris-baris pentingnya:
- Baris 9–11 (server membaca header): server mengakses
req.headers["content-type"]dengan nama huruf kecil. HTTP header tidak peka huruf besar-kecil, dan Node menyeragamkan semuanya ke huruf kecil di objectreq.headers— inilah bukti bahwaContent-Typeyang dikirimfetchbenar-benar tiba. - Baris 25–29 (options
headers): tiga header dikirim.Content-Typemendeskripsikan body,Acceptmenyatakan “saya hanya menerima JSON”, danX-Api-Keyadalah header kustom — konvensiX-awalan yang lazim untuk metadata aplikasi. Nilainya fiktif; pada server sungguhan, kredensial semacam ini dijaga rahasia dan tidak pernah ditulis di kode. - Baris 33 (
headers.get("Content-Type")):getjuga tidak peka huruf besar-kecil — menulis"content-type"memberi hasil yang sama. - Baris 35 (
headers.has(...)):hasmemeriksa keberadaan header tanpa perlu nilainya; berguna untuk header yang nilai jarang dibaca. - Baris 37–40 (iterasi header respons): object
Headersbisa diiterasi seperti map, dan hasilnya terurut abjad menurut nama — bukan urutan kiriman server. Bariscontinuemelewatidatekarena isinya berubah setiap saat dan akan membuat keluaran contoh tidak stabil. - Keluaran bagian bawah: selain dua header yang kita set di
writeHead,fetchmenampilkan header teknis bawaan protokol —connection,keep-alive,transfer-encoding— yang diurus Node tanpa kita campuri.
Accept dan Negosiasi Format #
Header Accept mengumumkan format yang bersedia diterima client. Server yang patuh akan menjawab dalam format itu — atau dengan 406 Not Acceptable bila tidak mampu. Dalam praktik API JSON modern, Accept: application/json sering bersifat dokumentatif karena hampir semua endpoint memang menyerahkan JSON; header ini justru krusial saat satu endpoint mampu melayani banyak format, misalnya text/html untuk browser dan application/json untuk skrip.
Pola negosiasi dua arah yang seimbang terlihat dari keluaran berkas contoh: request mengumumkan Accept: application/json, response mengabulkan lewat Content-Type: application/json; charset=utf-8.
Membaca Response Headers sebagai Object Headers #
response.headers adalah instance Headers — interface standar dengan tiga cara membaca:
headers.get("nama")— nilai satu header, ataunullbila tidak ada.headers.has("nama")— boolean keberadaan.- iterasi
for (const [nama, nilai] of headers)atauheaders.forEach(...)— seluruh pasangan, terurut abjad.
Untuk keputusan parsing, pola yang tangguh memeriksa Content-Type sebelum memilih pembaca body:
const response = await fetch(url);
const tipe = response.headers.get("content-type") ?? "";
if (tipe.includes("application/json")) {
const data = await response.json();
console.log("JSON:", data);
} else {
const teks = await response.text();
console.log("bukan JSON:", teks.slice(0, 40));
}Polanya memanfaatkan includes karena Content-Type sering menyertakan parameter tambahan seperti charset=utf-8 di belakang tipe utamanya. Cuplikan ini mengikuti berkas contoh: berjalan terhadap server tiruan yang sama, keluarannya JSON: { id: 17, judul: 'Kamus Bahasa' } bila server mengirim JSON.
Header yang fetch Kirim Otomatis #
Bahkan tanpa options headers, fetch tetap melampirkan beberapa header sendiri. Berkas contoh tadi membuktikannya lewat sisi server; isi khasnya:
| Header | Nilai di contoh Node | Peran |
|---|---|---|
accept | */* | Menerima format apa pun |
user-agent | node | Identitas aplikasi pengirim |
connection | keep-alive | Kelola pemakaian ulang koneksi |
accept-encoding | gzip, deflate | Format kompresi yang dipahami |
Di browser, daftar ini bertambah dua-duanya: user-agent berisi identitas browser yang panjang, dan sec-fetch-mode: cors menyertai permintaan lintas origin sebagai penanda konteks keamanan. Header otomatis ini bisa ditimpa — menambah Accept: application/json pada options, seperti pada berkas contoh, menggantikan */* bawaan.
Kapan Header Kustom Dipakai #
Header kustom adalah saluran metadata aplikasi yang tak disediakan protokol. Tiga kebiasaan industri yang layak ditiru:
- Identitas permintaan — server diberi
X-Request-Iddari client, lalu mencantumkannya di log; saat galat dilaporkan pengguna, id itu memudahkan pencarian jejak permintaan di sisi server. - Penanda integrasi —
X-Api-Keyuntuk mengenali aplikasi pemanggil, sebagaimana disimulasikan berkas contoh. - Informasi balik — server pun boleh berekspresi lewat header kustom, misalnya
X-RateLimit-Remaining: 42yang memberi tahu sisa kuota permintaan sebelum429mengetuk.
Pemakaian header kustom pada permintaan lintas origin punya konsekuensi tambahan: browser menuntut header itu terdaftar di Access-Control-Allow-Headers respons preflight — persis seperti yang dilakukan server tiruan di berkas contoh untuk X-Api-Key.
Header bukan sekadar label format — sebagian header menentukan keamanan percakapan lintas origin. Itulah topik chapter CORS berikutnya.