Sepuluh tahun silam, JavaScript di browser dan di server adalah dua dunia dengan kosakata berbeda: XMLHttpRequest lawan http.request, window.location lawan url.parse. Kode tidak bisa berpindah tanpa diterjemahkan. Hari ini garis itu nyaris hilang — Node.js mengadopsi antarmuka standar web yang sama dengan browser: fetch, Request, Response, Headers, URL, FormData, hingga structuredClone. Bab penutup section ini memeriksa apa saja yang kini menjadi bawaan, memakainya dalam tiga kelompok kerja, lalu melihat mengapa konvergensi ini mengubah cara kita menulis kode yang bisa hidup di kedua dunia.

Inventaris di Tangan Sendiri #

Cara paling jujur mengenal apa yang tersedia: bertanya kepada runtime. Program berikut memeriksa typeof dari dua belas nama yang akrab dari dunia browser:

js
// Memeriksa global Web API yang tersedia di Node.js
const daftar = [
  'fetch', 'Request', 'Response', 'Headers',
  'FormData', 'Blob', 'URL', 'URLSearchParams',
  'structuredClone', 'crypto', 'BroadcastChannel', 'WebSocket',
  'performance', 'TextEncoder', 'localStorage',
];

for (const nama of daftar) {
  console.log(nama.padEnd(18), ':', typeof globalThis[nama]);
}

Output yang Dihasilkan:

text
fetch              : function
Request            : function
Response           : function
Headers            : function
FormData           : function
Blob               : function
URL                : function
URLSearchParams    : function
structuredClone    : function
crypto             : object
BroadcastChannel   : function
WebSocket          : function
performance        : object
TextEncoder        : function
localStorage       : undefined

Hampir semuanya function atau object — hidup sebagai global tanpa satu import pun. Dua tetap bergantung pada konteks: crypto berupa object, dan localStorage undefined — penyimpanan per-origin memang milik lingkungan browser, bukan runtime server. Daftar resmi global di Node tersedia di dokumentasi globals , sedangkan makna standar tiap API dijelaskan di MDN Web APIs .

URL dan URLSearchParams #

Kelompok pertama bekerja pada alamat: mengurai, mengubah, dan menyusunnya. Kedua kelas ini sudah lama hadir di Node dan kita pakai berulang kali sepanjang Course — inilah waktunya melihat mereka sebagai satu paket:

js
// Mengurai dan menyusun URL dengan Web API
const url = new URL(`https://nodejs.org/api/fs.html`);

console.log('protocol:', url.protocol);
console.log('host    :', url.host);
console.log('pathname:', url.pathname);

// searchParams dapat diubah langsung seperti Map
url.searchParams.set('search', 'readFile');
url.searchParams.append('halaman', '2');
console.log('hasil   :', url.toString());

// Menyusun query string dari object
const kueri = new URLSearchParams({ kota: 'Bandung', halaman: '2' });
kueri.append('kota', 'Surabaya');
console.log('kota    :', kueri.getAll('kota').join(' & '));
console.log('string  :', kueri.toString());

Output yang Dihasilkan:

text
protocol: https:
host    : nodejs.org
pathname: /api/fs.html
hasil   : https://nodejs.org/api/fs.html?search=readFile&halaman=2
kota    : Bandung & Surabaya
string  : kota=Bandung&halaman=2&kota=Surabaya

Dua hal patut digarisbawahi. Pertama, url.searchParams bukan sekadar pembaca: set dan append mengubah kueri di tempat, dan toString() merangkai hasilnya — sama persis seperti manipulasi Map. Kedua, getAll mengembalikan seluruh nilai untuk kunci yang berulang (kota muncul dua kali) — perilaku yang kita manfaatkan pada query parsing di Section REST API.

Alat di Luar HTTP #

Kelompok kedua tidak peduli HTTP sama sekali. structuredClone menyalin objek secara dalam — termasuk Date dan struktur bersarang — sesuatu yang sering salah dilakukan dengan operator spread. FormData dan Blob membentuk pasangan kunci-nilai untuk unggahan. crypto.randomUUID membuat pengenal unik tanpa memutar mesin acak sendiri:

js
// structuredClone: salinan dalam yang mempertahankan tipe
const asli = {
  judul: 'Laporan Harian',
  dibuat: new Date(Date.UTC(2026, 0, 15, 8, 0, 0)),
  penulis: { nama: 'Budi', peran: ['admin', 'editor'] },
};

const salinan = structuredClone(asli);
salinan.penulis.peran.push('pembaca');
salinan.judul = 'Laporan Diubah';

console.log('asli    :', asli.judul, '|', asli.penulis.peran.join(','));
console.log('salinan :', salinan.judul, '|', salinan.penulis.peran.join(','));
console.log('tanggal :', salinan.dibuat instanceof Date, salinan.dibuat.toISOString());

// FormData: pasangan kunci-nilai untuk unggahan
const formulir = new FormData();
formulir.append('nama', 'Budi');
formulir.append('lampiran', new Blob(['isi berkas'], { type: 'text/plain' }), 'catatan.txt');
for (const [kunci, nilai] of formulir.entries()) {
  console.log('form    :', kunci, '=', typeof nilai === 'string' ? nilai : nilai.name);
}

// crypto.randomUUID: pengenal unik versi 4
const id = crypto.randomUUID();
const pola = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
console.log('uuid    : cocok pola versi 4 =', pola.test(id));

Output yang Dihasilkan:

text
asli    : Laporan Harian | admin,editor
salinan : Laporan Diubah | admin,editor,pembaca
tanggal : true 2026-01-15T08:00:00.000Z
form    : nama = Budi
form    : lampiran = catatan.txt
uuid    : cocok pola versi 4 = true

Perhatikan bukti salinan dalam: salinan mendapat peran pembaca dan judul baru, sementara asli tak tersentuh — dengan spread operator ({...asli}), array peran yang bersarang justru ikut terbagi. Keluaran UUID tidak dicetak nilainya — UUID memang berbeda tiap pemanggilan — melainkan dicek polanya: panjang tepat, versi 4, huruf heksadesimal.

Request, Headers, dan fetch #

Kelompok ketiga adalah wajah HTTP itu sendiri. Trik terbesarnya: fetch tidak menuntut string alamat — ia menerima object Request yang sudah dirakit lengkap dengan method, header, dan body:

js
import http from 'node:http';

// Peladen latihan: menjawab dengan potongan permintaan yang diterima
const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://127.0.0.1`);
  let body = '';
  req.on('data', (potongan) => (body += potongan));
  req.on('end', () => {
    const data = JSON.parse(body);
    res.writeHead(201, {
      'Content-Type': 'application/json; charset=utf-8',
      'X-Dibuat-Oleh': 'peladen-latihan',
    });
    res.end(JSON.stringify({
      lokasi: url.pathname,
      filter: url.searchParams.get('status'),
      judul: data.judul,
      izin: req.headers['x-izin'],
    }));
  });
});

server.listen(0, '127.0.0.1', async () => {
  const port = server.address().port;

  // URL + URLSearchParams menyusun alamat lengkap
  const kueri = new URLSearchParams({ status: 'aktif' });
  const target = new URL(`http://127.0.0.1:${port}/tugas?${kueri}`);

  // Headers menampung header permintaan
  const headers = new Headers({
    'Content-Type': 'application/json',
    'X-Izin': 'budi-2026',
  });

  // Request merangkai method, header, dan body
  const permintaan = new Request(target, {
    method: 'POST',
    headers,
    body: JSON.stringify({ judul: 'Belajar Web API' }),
  });

  // fetch menerima object Request langsung
  const jawaban = await fetch(permintaan);
  console.log('status :', jawaban.status, jawaban.ok);
  console.log('header :', jawaban.headers.get('x-dibuat-oleh'));
  console.log('body   :', JSON.stringify(await jawaban.json()));

  server.close();
});

Output yang Dihasilkan:

text
status : 201 true
header : peladen-latihan
body   : {"lokasi":"/tugas","filter":"aktif","judul":"Belajar Web API","izin":"budi-2026"}

Alur penuhnya terbaca dari keluaran: URL dan URLSearchParams menyusun alamat, Headers merangkai kepala permintaan, Request memaket semuanya dengan method dan body, dan fetch mengirimkannya. Peladen membalas 201 dengan header buatannya sendiri — dibaca kembali lewat jawaban.headers.get() dengan nama lowercase, sebagaimana standar HTTP menormalkan nama header. Empat baris terakhir keluaran adalah bukti bahwa seluruh rangkaian benar-benar sampai dan kembali.

Mengapa Konvergensi Ini Penting #

Nilai sesungguhnya bukan “Node punya fungsi baru”, melainkan kode yang bisa berpindah. Fungsi validasi form yang memakai URLSearchParams bekerja identik di browser dan di server; builder Request untuk pengujian integrasi memakai kelas yang sama dengan yang dipakai service worker di browser; dokumentasi MDN untuk satu API berlaku untuk dua dunia. Bagi Course ini, konvergensi itu menjelaskan pilihan-pilihan yang sudah kita ambil: fetch dipakai sejak Section Networking tanpa pernah membahas dua API berbeda, dan pola listen(0) + fetch yang menghiasi seluruh demonstrasi adalah anak langsung dari keputusan ini.

Dengan ini perjalanan materi inti Course selesai: dari console.log pertama hingga runtime yang menjalankan TypeScript, membatasi izinnya sendiri, dan memakai bahasa bersama dengan browser. Yang tersisa adalah peta agar perjalanan lanjutan mandiri — dokumentasi resmi, cara membacanya, glosarium, dan panduan rujukan — yang menanti pada Section terakhir: Sumber Belajar.