Teori protokol pada chapter sebelumnya sekarang kita turunkan ke kode. Dengan module http, sebuah web server yang mampu melayani request sesungguhnya hanya butuh satu panggilan function: http.createServer(). Framework seperti Express dan Fastify pada akhirnya berdiri di atas module yang sama, jadi memahami lapisan dasar ini membuat kita memahami apa yang sebenarnya terjadi di balik framework kesayangan kita. Pada chapter ini kita bangun server pertama, bedah object req dan res, lalu lihat perbedaan response teks dan JSON.

Server Pertama Kita #

File berikut membuat server, menjalankannya, lalu memeriksa hasilnya dengan dua fetch berurutan — pola uji mandiri yang akan dipakai berulang kali di Section ini. Dokumentasi resmi untuk seluruh API yang dipakai tersedia di halaman API node:http :

js
import http from 'node:http';

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end('Halo dari server pertama kita!');
  } else {
    res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({ jalur: req.url, method: req.method }));
  }
});

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

  const r1 = await fetch(`http://127.0.0.1:${port}/`);
  console.log('respons 1:', r1.status, r1.headers.get('content-type'));
  console.log('isi      :', await r1.text());

  const r2 = await fetch(`http://127.0.0.1:${port}/tugas/42`);
  console.log('respons 2:', r2.status, r2.headers.get('content-type'));
  console.log('isi      :', JSON.stringify(await r2.json()));

  server.close();
});

Output yang Dihasilkan:

text
respons 1: 200 text/plain; charset=utf-8
isi      : Halo dari server pertama kita!
respons 2: 200 application/json; charset=utf-8
isi      : {"jalur":"/tugas/42","method":"GET"}

Mari kita telaah baris-baris kode di atas:

  1. Baris 1 (import http from 'node:http'): module http bawaan Node.js dimuat tanpa perlu npm install apa pun.
  2. Baris 3 (http.createServer((req, res) => {...}): kita mendaftarkan satu request handler — function yang dipanggil setiap kali ada request masuk, persis seperti opsi requestListener pada http.createServer() . Parameter req (object IncomingMessage) merepresentasikan request, sedangkan res (object ServerResponse) adalah alat kita menulis response.
  3. Baris 4-6: untuk jalur /, server membalas teks biasa. writeHead(200, {...}) menetapkan status code sekaligus header, lalu end('...') mengirim body dan menutup response.
  4. Baris 7-10: jalur lain dibalas dengan JSON. Object JavaScript diubah menjadi string lewat JSON.stringify karena body HTTP selalu berupa urutan byte, bukan object.
  5. Baris 13 (server.listen(0, '127.0.0.1', ...)): server mulai mendengarkan koneksi. Port 0 adalah kode khusus yang meminta sistem operasi memilih port acak yang kosong — trik yang membuat contoh Course ini bisa dijalankan berulang tanpa bentrok port. Callback berjalan setelah server siap.
  6. Baris 14 (server.address().port): kita membaca port hasil pilihan sistem operasi, lalu memakainya di URL fetch.
  7. Baris 16-22: dua request dijalankan berurutan. Kita mencetak status, header Content-Type, dan isi body dari masing-masing response — hanya nilai yang memang kita atur sendiri.
  8. Baris 24 (server.close()): server dimatikan agar proses Node.js bisa keluar dengan bersih. Tanpa baris ini, event loop tetap menunggu koneksi baru dan program tidak pernah selesai.

Mengenal Object req dan res #

Dua object ini adalah pintu masuk seluruh logika server kita. Properti dan method yang paling sering dipakai:

SisiAnggotaIsi
reqreq.urlJalur beserta query string dari request line, misalnya /tugas/42
reqreq.methodMethod request sebagai string huruf kapital: GET, POST, PUT, DELETE
reqreq.headersObject berisi seluruh header request, misalnya req.headers['content-type']
resres.writeHead(status, headers)Menetapkan status code dan header sekaligus, lalu mulai menulis
resres.setHeader(nama, nilai)Menetapkan satu header tanpa mengirim apa pun
resres.write(potongan)Mengirim sebagian body — bisa dipanggil berkali-kali
resres.end(body)Mengirim sisa body (bila ada) dan menandai response selesai

Perlu dicatat bahwa req.url berupa string mentah, bukan object URL. Saat kita butuh jalur, query string, atau path parameter yang terurai rapi, kita akan mengurai string tersebut dengan new URL() pada Chapter Routing dan Request — dan req sendiri adalah sebuah readable stream yang akan kita baca seperti stream pada Chapter Membaca Request Body.

Teks, JSON, dan Peran Content-Type #

Server pada demo membalas dua format berbeda, dan keduanya dibedakan lewat header Content-Type. Untuk teks, kita memakai text/plain; untuk data terstruktur, application/json. Sufiks ; charset=utf-8 memastikan penerima menerjemahkan byte sebagai teks UTF-8 sehingga karakter Indonesia kita tetap utuh.

Header kecil ini menentukan cara klien memperlakukan body. Browser menampilkan text/plain sebagai teks polos, mencoba merender text/html sebagai halaman web, dan mem-parsing application/json untuk kebutuhan JavaScript. Karena fetch di sisi klien menyediakan dua pembaca berbeda — r.text() untuk teks dan r.json() untuk JSON — demo memakai pembaca yang sesuai dengan format masing-masing response; hasil r.json() langsung menjadi object JavaScript kembali, terlihat dari JSON.stringify yang mencetaknya rapi.

Siklus Hidup Proses Server #

Urutan hidup server pada demo: createServer mendaftarkan handler, listen membuka port, handler berjalan tiap ada request, dan close menutup port. Pada server produksi yang sesungguhnya, kita memakai port tetap semisal 3000 dan tidak memanggil close() — proses dibiarkan berjalan melayani request tanpa akhir, dan proses dihentikan lewat sinyal seperti Ctrl+C. Pola listen(0) + close() adalah pola uji otomatis, bukan pola produksi.

Dengan server dasar di tangan, satu keterbatasan langsung terasa: semua jalur dibedakan lewat if (req.url === ...) yang akan tumbuh membingungkan seiring bertambahnya endpoint. Chapter Routing dan Request memperkenalkan struktur yang lebih rapi — tabel rute dengan Map dan penguraian jalur dinamis seperti /tugas/42.