Back to Blog
Node.jsdnsNetworkingResolution

Node.js dns Module Guide

Master the Node.js dns module for DNS resolution, lookups, and network operations.

B
Bootspring Team
Engineering
February 16, 2019
6 min read

The dns module provides DNS resolution and lookup capabilities. Here's how to use it for network operations.

Basic Lookup#

1import dns from 'node:dns'; 2import { promisify } from 'node:util'; 3 4const lookup = promisify(dns.lookup); 5 6// Simple lookup 7async function resolveHostname(hostname) { 8 try { 9 const result = await lookup(hostname); 10 console.log('Address:', result.address); 11 console.log('Family:', result.family); // 4 or 6 12 return result; 13 } catch (error) { 14 console.error('Lookup failed:', error.message); 15 throw error; 16 } 17} 18 19await resolveHostname('google.com'); 20// Address: 142.250.80.46 21// Family: 4

Promise-based API#

1import dns from 'node:dns/promises'; 2 3// Use promises API directly 4async function dnsOperations() { 5 // Lookup 6 const address = await dns.lookup('example.com'); 7 console.log('IP:', address.address); 8 9 // Resolve multiple record types 10 const mx = await dns.resolveMx('gmail.com'); 11 console.log('MX records:', mx); 12 13 const txt = await dns.resolveTxt('google.com'); 14 console.log('TXT records:', txt); 15}

Lookup Options#

1import dns from 'node:dns/promises'; 2 3// IPv4 only 4const ipv4 = await dns.lookup('google.com', { family: 4 }); 5console.log('IPv4:', ipv4.address); 6 7// IPv6 only 8const ipv6 = await dns.lookup('google.com', { family: 6 }); 9console.log('IPv6:', ipv6.address); 10 11// All addresses 12const all = await dns.lookup('google.com', { all: true }); 13console.log('All addresses:', all); 14// [{ address: '...', family: 4 }, { address: '...', family: 6 }] 15 16// With hints 17const hints = await dns.lookup('google.com', { 18 hints: dns.ADDRCONFIG | dns.V4MAPPED, 19});

Resolve DNS Records#

1import dns from 'node:dns/promises'; 2 3// A records (IPv4) 4async function resolveA(hostname) { 5 const addresses = await dns.resolve4(hostname); 6 console.log('A records:', addresses); 7 return addresses; 8} 9 10// AAAA records (IPv6) 11async function resolveAAAA(hostname) { 12 const addresses = await dns.resolve6(hostname); 13 console.log('AAAA records:', addresses); 14 return addresses; 15} 16 17// MX records (mail) 18async function resolveMX(hostname) { 19 const records = await dns.resolveMx(hostname); 20 console.log('MX records:', records); 21 // [{ exchange: 'mail.example.com', priority: 10 }] 22 return records.sort((a, b) => a.priority - b.priority); 23} 24 25// TXT records 26async function resolveTXT(hostname) { 27 const records = await dns.resolveTxt(hostname); 28 console.log('TXT records:', records); 29 return records.flat(); 30} 31 32// NS records (nameservers) 33async function resolveNS(hostname) { 34 const nameservers = await dns.resolveNs(hostname); 35 console.log('NS records:', nameservers); 36 return nameservers; 37} 38 39// CNAME records 40async function resolveCNAME(hostname) { 41 try { 42 const cname = await dns.resolveCname(hostname); 43 console.log('CNAME:', cname); 44 return cname; 45 } catch (error) { 46 if (error.code === 'ENODATA') { 47 return null; // No CNAME record 48 } 49 throw error; 50 } 51}

SOA Records#

1import dns from 'node:dns/promises'; 2 3async function getSOA(domain) { 4 const soa = await dns.resolveSoa(domain); 5 console.log('SOA Record:'); 6 console.log(' Primary NS:', soa.nsname); 7 console.log(' Admin:', soa.hostmaster); 8 console.log(' Serial:', soa.serial); 9 console.log(' Refresh:', soa.refresh); 10 console.log(' Retry:', soa.retry); 11 console.log(' Expire:', soa.expire); 12 console.log(' Min TTL:', soa.minttl); 13 return soa; 14} 15 16await getSOA('google.com');

SRV Records#

1import dns from 'node:dns/promises'; 2 3async function resolveSRV(service) { 4 const records = await dns.resolveSrv(service); 5 6 // Sort by priority, then weight 7 records.sort((a, b) => { 8 if (a.priority !== b.priority) { 9 return a.priority - b.priority; 10 } 11 return b.weight - a.weight; 12 }); 13 14 console.log('SRV records:'); 15 records.forEach((r) => { 16 console.log(` ${r.name}:${r.port} (priority: ${r.priority}, weight: ${r.weight})`); 17 }); 18 19 return records; 20} 21 22// Example: XMPP server discovery 23await resolveSRV('_xmpp-server._tcp.gmail.com');

Reverse DNS Lookup#

1import dns from 'node:dns/promises'; 2 3async function reverseLookup(ip) { 4 try { 5 const hostnames = await dns.reverse(ip); 6 console.log(`${ip} resolves to:`, hostnames); 7 return hostnames; 8 } catch (error) { 9 if (error.code === 'ENOTFOUND') { 10 console.log(`No PTR record for ${ip}`); 11 return []; 12 } 13 throw error; 14 } 15} 16 17await reverseLookup('8.8.8.8'); 18// ['dns.google']

Resolve Any Record Type#

1import dns from 'node:dns/promises'; 2 3async function resolveAny(hostname) { 4 try { 5 const records = await dns.resolveAny(hostname); 6 7 records.forEach((record) => { 8 console.log(`${record.type}:`, record); 9 }); 10 11 return records; 12 } catch (error) { 13 console.error('Failed to resolve:', error.message); 14 throw error; 15 } 16} 17 18// Get all available records 19await resolveAny('google.com');

Custom DNS Servers#

1import dns from 'node:dns'; 2import { Resolver } from 'node:dns/promises'; 3 4// Create resolver with custom servers 5const resolver = new Resolver(); 6resolver.setServers(['8.8.8.8', '8.8.4.4']); // Google DNS 7 8async function lookupWithCustomDNS(hostname) { 9 const addresses = await resolver.resolve4(hostname); 10 console.log('Using custom DNS:', addresses); 11 return addresses; 12} 13 14// Get current DNS servers 15console.log('Current servers:', dns.getServers()); 16 17// Set servers globally (affects all lookups) 18dns.setServers(['1.1.1.1', '1.0.0.1']); // Cloudflare DNS

DNS Cache#

1import dns from 'node:dns/promises'; 2 3class DNSCache { 4 constructor(ttl = 60000) { 5 this.cache = new Map(); 6 this.ttl = ttl; 7 } 8 9 async lookup(hostname, options = {}) { 10 const key = `${hostname}-${options.family || 'any'}`; 11 const cached = this.cache.get(key); 12 13 if (cached && Date.now() < cached.expires) { 14 console.log('Cache hit:', hostname); 15 return cached.result; 16 } 17 18 console.log('Cache miss:', hostname); 19 const result = await dns.lookup(hostname, options); 20 21 this.cache.set(key, { 22 result, 23 expires: Date.now() + this.ttl, 24 }); 25 26 return result; 27 } 28 29 async resolve4(hostname) { 30 const key = `A-${hostname}`; 31 const cached = this.cache.get(key); 32 33 if (cached && Date.now() < cached.expires) { 34 return cached.result; 35 } 36 37 const result = await dns.resolve4(hostname); 38 this.cache.set(key, { 39 result, 40 expires: Date.now() + this.ttl, 41 }); 42 43 return result; 44 } 45 46 clear() { 47 this.cache.clear(); 48 } 49} 50 51const dnsCache = new DNSCache(300000); // 5 minute TTL

Health Check with DNS#

1import dns from 'node:dns/promises'; 2 3async function checkDNSHealth(hostname, expectedIPs = []) { 4 const startTime = Date.now(); 5 6 try { 7 const result = await dns.lookup(hostname, { all: true }); 8 const duration = Date.now() - startTime; 9 10 const ips = result.map((r) => r.address); 11 const hasExpected = expectedIPs.every((ip) => ips.includes(ip)); 12 13 return { 14 hostname, 15 healthy: true, 16 duration, 17 addresses: ips, 18 expectedMatch: hasExpected, 19 }; 20 } catch (error) { 21 return { 22 hostname, 23 healthy: false, 24 duration: Date.now() - startTime, 25 error: error.message, 26 code: error.code, 27 }; 28 } 29} 30 31async function monitorDNS(hostnames, interval = 30000) { 32 setInterval(async () => { 33 for (const hostname of hostnames) { 34 const health = await checkDNSHealth(hostname); 35 if (!health.healthy) { 36 console.error(`DNS health check failed for ${hostname}:`, health); 37 } 38 } 39 }, interval); 40}

Error Handling#

1import dns from 'node:dns/promises'; 2 3async function safeLookup(hostname) { 4 try { 5 return await dns.lookup(hostname); 6 } catch (error) { 7 switch (error.code) { 8 case 'ENOTFOUND': 9 console.error(`Hostname not found: ${hostname}`); 10 break; 11 case 'ENODATA': 12 console.error(`No data for: ${hostname}`); 13 break; 14 case 'ETIMEOUT': 15 console.error(`DNS timeout for: ${hostname}`); 16 break; 17 case 'ESERVFAIL': 18 console.error(`DNS server failure for: ${hostname}`); 19 break; 20 case 'ECONNREFUSED': 21 console.error(`DNS connection refused for: ${hostname}`); 22 break; 23 default: 24 console.error(`DNS error for ${hostname}:`, error.message); 25 } 26 throw error; 27 } 28} 29 30// With retry 31async function lookupWithRetry(hostname, retries = 3, delay = 1000) { 32 for (let i = 0; i < retries; i++) { 33 try { 34 return await dns.lookup(hostname); 35 } catch (error) { 36 if (i === retries - 1) throw error; 37 await new Promise((r) => setTimeout(r, delay)); 38 } 39 } 40}

Best Practices#

Lookup vs Resolve: ✓ lookup() uses OS resolver (cached) ✓ resolve() queries DNS directly ✓ Use lookup() for general use ✓ Use resolve() for specific records Performance: ✓ Cache DNS results ✓ Use connection pooling ✓ Set reasonable timeouts ✓ Handle failures gracefully Error Handling: ✓ Check error.code ✓ Implement retries ✓ Log DNS failures ✓ Have fallback servers Avoid: ✗ Hardcoding IPs ✗ Ignoring TTLs ✗ Blocking on DNS ✗ No timeout handling

Conclusion#

The Node.js dns module provides comprehensive DNS resolution capabilities. Use lookup() for general hostname resolution with OS caching, and resolve*() methods for specific record types. Implement caching for performance, handle errors with retry logic, and use custom resolvers for specific DNS servers. The promises API makes async DNS operations clean and composable for modern Node.js applications.

Share this article

Help spread the word about Bootspring