Sepanjang Section ini kita selalu menyambung ke 127.0.0.1 — alamat yang bisa dihafal. Aplikasi nyata berbicara lewat nama seperti example.com, dan ada satu subsistem khusus yang menerjemahkan nama menjadi alamat IP: DNS. Module node:dns memberi kita akses ke subsistem tersebut dari JavaScript, mulai dari dns.lookup() yang dipakai diam-diam oleh net.connect(), sampai dns.resolve4() yang berbicara langsung ke server DNS. Pada chapter ini kita bedah dua keluarga fungsi tersebut, memahami pilihan IPv4 atau IPv6 lewat opsi family, serta menangani nama yang gagal diterjemahkan dengan rapi.

Nama Menjadi Alamat dengan lookup #

Fungsi yang paling sering kita pakai — sering tanpa sadar — adalah dns.lookup() . Fungsi ini bertanya ke resolver sistem operasi (panggilan getaddrinfo), sumber yang sama dengan yang dipakai program lain di komputer kita; karena itu hasilnya mengikuti berkas hosts lokal dan pengaturan jaringan mesin. Simpan kode berikut sebagai dns-lookup.mjs:

js
import dns from 'node:dns/promises';

const main = async () => {
  const bawaan = await dns.lookup('localhost');
  console.log('lookup bawaan :', bawaan.address, `(family ${bawaan.family})`);

  const hanyaV4 = await dns.lookup('localhost', { family: 4 });
  console.log('family 4     :', hanyaV4.address, `(family ${hanyaV4.family})`);

  const hanyaV6 = await dns.lookup('localhost', { family: 6 });
  console.log('family 6     :', hanyaV6.address, `(family ${hanyaV6.family})`);

  const semua = await dns.lookup('localhost', { all: true });
  for (const entri of semua) {
    console.log('entri all    :', entri.address, `(family ${entri.family})`);
  }
};

main();

Output yang Dihasilkan:

text
lookup bawaan : ::1 (family 6)
family 4     : 127.0.0.1 (family 4)
family 6     : ::1 (family 6)
entri all    : ::1 (family 6)
entri all    : 127.0.0.1 (family 4)

Ada dua hal yang perlu dicermati dari output di atas. Pertama, versi promise API lewat import dns from 'node:dns/promises' membuat kode berurutan rapi tanpa callback; versi callback tetap tersedia di import dns from 'node:dns' dengan nama fungsi yang sama. Kedua, hasil lookup bawaan untuk localhost di mesin ini adalah ::1 dengan family 6 — alamat IPv6 — padahal kita biasa menulis 127.0.0.1. Urutan ini berasal dari berkas hosts yang menuliskan ::1 lebih dulu; secara bawaan Node.js mempertahankan urutan yang diberikan sistem operasi.

Mari kita bedah empat panggilan dalam berkas tersebut:

  1. Baris 4 (dns.lookup('localhost')): bentuk paling sederhana — satu nama, satu jawaban. Object hasil memuat address dan family, dan family 6 di sini menandakan jawaban berupa alamat IPv6.
  2. Baris 7 ({ family: 4 }): memaksa resolver hanya menerima alamat IPv4, sehingga jawabannya 127.0.0.1 alih-alih ::1.
  3. Baris 10 ({ family: 6 }): kebalikannya — hanya IPv6. Jika tidak ada alamat dengan family yang diminta, promise ditolak dengan error.
  4. Baris 13-16 ({ all: true }): meminta semua kandidat sekaligus sebagai array, lalu loop for mencetak satu per satu. Inilah bentuk yang tepat saat kita ingin mencoba menyambung ke beberapa alamat secara berurutan.

Opsi family adalah alat kendali kita karena socket IPv4 tidak bisa menyambung ke alamat IPv6 dan sebaliknya — protokol transport yang kita pilih di createSocket('udp4') atau koneksi TCP harus cocok dengan family alamat hasil resolusi.

lookup versus resolve #

Module node:dns menyimpan satu kebingungan klasik: ada lookup dan ada keluarga resolve. Keduanya sama-sama menerjemahkan nama, tetapi jalurnya berbeda total:

Aspekdns.lookup()dns.resolve4() dan keluarganya
Sumber jawabanresolver sistem operasi (getaddrinfo)koneksi langsung ke server DNS
Berkas hosts lokaldiikutidilewati
Bentuk hasilsatu alamat (atau array dengan all: true)array semua record yang ditemukan
Jenis recordhanya alamat A/AAAAper fungsi: MX, TXT, NS, SRV, dan lainnya
Kapan dipakaisebelum connect, meniru perilaku program lainbutuh record lengkap atau jawaban mentah DNS

Dalam praktik sehari-hari lookup adalah pilihan tepat karena net.connect() memang memanggilnya secara internal setiap kali kita memberikan hostname alih-alih alamat IP. Keluarga resolve dipakai saat kita membangun alat yang benar-benar berbicara dengan DNS — memeriksa record MX sebuah domain, atau membuat alat diagnostik seperti dig. Karena resolve mengirim query ke server DNS sungguhan, contoh berikut hanya ilustrasi dan tidak dieksekusi di buku ini:

js Contoh resolve4 (membutuhkan akses jaringan)
import dns from 'node:dns/promises';

const alamat = await dns.resolve4('example.com');
console.log(alamat);
// contoh bentuk hasil: [ '93.184.216.34' ]

Untuk kebutuhan yang lebih terarah, class dns.promises.Resolver memungkinkan kita membuat resolver dengan server DNS sendiri — berguna saat menguji infrastruktur DNS atau mengisolasi query dari pengaturan mesin.

Ketika Nama Tidak Ditemukan #

DNS adalah layanan jaringan, jadi kegagalan adalah bagian dari kontraknya. Nama yang salah, domain yang sudah tidak terdaftar, atau koneksi resolver yang bermasalah semuanya datang sebagai error dari promise lookup. Berkas dns-gagal.mjs memperlihatkan penanganannya:

js
import dns from 'node:dns/promises';

const main = async () => {
  try {
    const hasil = await dns.lookup('domain-tidak-ada.example.invalid');
    console.log('tidak akan pernah tercetak:', hasil.address);
  } catch (err) {
    console.log('kode error  :', err.code);
    console.log('pesan error :', err.message);
  }
};

main();

Output yang Dihasilkan:

text
kode error  : ENOTFOUND
pesan error : getaddrinfo ENOTFOUND domain-tidak-ada.example.invalid

Kode ENOTFOUND berarti resolver tidak mengenal nama tersebut — akibat yang paling sering terjadi. Sebagai pembanding, EAI_AGAIN menandakan kegagalan sementara seperti resolver yang tidak merespons, sehingga mencoba lagi setelah beberapa saat adalah respons yang wajar. Akhiran .invalid pada contoh sengaja dipilih karena spesifikasi internet mencadangkannya untuk nama yang pasti tidak ada, sehingga contoh ini aman dijalankan tanpa bergantung pada kondisi jaringan. Pola try/catch dengan mencetak err.code — bukan seluruh stack — adalah kebiasaan yang sudah kita kenal dari Section Asynchronous Runtime.

Peta kita kini lengkap untuk sisi alamat: TCP dan UDP untuk bertukar data, DNS untuk menerjemahkan nama. Tersisa satu keterampilan yang menyatukan semuanya menjadi aplikasi sungguhan: bagaimana jika client dan server kita sendiri yang menentukan format percakapan? Chapter penutup Section ini, Membangun Protokol Berbasis Baris, memandu kita merancang protokol teks pertama di atas TCP.