Node.js DNS Module
Introduction to the DNS Module
The DNS (Domain Name System) module provides functionality for name resolution in Node.js.
It offers two main APIs:
- Callback-based API: Traditional Node.js style with callback functions
- Promise-based API: Modern async/await support via
dns.promises
Key features include:
- Resolving domain names to IP addresses (A/AAAA records)
- Performing reverse DNS lookups (PTR records)
- Querying various DNS record types (MX, TXT, SRV, etc.)
- Creating custom DNS resolvers with specific settings
- Configuring DNS server settings programmatically
Note: The DNS module can operate in two distinct modes - using the operating system's facilities or performing direct network DNS queries.
This affects how hostname resolution works in your application.
Getting Started with DNS
Here's a quick example of using the DNS module to look up a domain's IP address:
Basic DNS Lookup
const dns = require('dns');
// Look up a domain name
dns.lookup('example.com', (err, address, family) => {
if (err) {
console.error('Lookup error:', err);
return;
}
console.log(`IP address: ${address}`);
console.log(`IP version: IPv${family}`);
});
Run example »
Importing and Setup
To use the DNS module, you can import it in your Node.js application using either the callback or promise-based API:
Callback-based API
// Import the DNS module
const dns = require('dns');
// Example usage
dns.lookup('example.com', (err, address, family) => {
if (err) throw err;
console.log(`Resolved: ${address} (IPv${family})`);
});
Promise-based API (Node.js 10.0.0+)
// Import the promises API
const { promises: dns } = require('dns');
// Or: const dns = require('dns').promises;
// Example with async/await
async function lookupDomain(domain) {
try {
const address = await dns.lookup(domain);
console.log(`Resolved: ${address.address} (IPv${address.family})`);
} catch (err) {
console.error('Lookup failed:', err);
}
}
lookupDomain('example.com');
Note: The promise-based API is generally preferred for new code as it works better with modern async/await patterns and provides better error handling.
Basic DNS Lookups
The DNS module provides several methods for looking up domain names and IP addresses. The most common operations are:
dns.lookup()
: Uses the operating system's facilities to resolve hostnamesdns.resolve*()
: Performs DNS queries directly to name serversdns.reverse()
: Performs reverse DNS lookups (IP to hostname)
Resolving Domain Names to IP Addresses
const dns = require('dns');
// Callback-based API
dns.lookup('www.example.com', (err, address, family) => {
if (err) throw err;
console.log('IP address: %s', address);
console.log('IP version: IPv%s', family);
});
Run example »
const dns = require('dns').promises;
// Promise-based API
async function lookupExample() {
try {
const result = await dns.lookup('www.example.com');
console.log('IP address:', result.address);
console.log('IP version: IPv' + result.family);
} catch (err) {
console.error('Lookup failed:', err);
}
}
lookupExample();
Run example »
Note: The dns.lookup()
method uses the operating system's facilities for name resolution and does not necessarily perform any network communication.
Looking Up All IP Addresses for a Domain
const dns = require('dns');
// Get all IPv4 addresses
dns.resolve4('www.google.com', (err, addresses) => {
if (err) throw err;
console.log('IPv4 addresses:');
addresses.forEach(address => {
console.log(` ${address}`);
});
// Perform a reverse lookup on the first IP
dns.reverse(addresses[0], (err, hostnames) => {
if (err) throw err;
console.log(`Reverse lookup for ${addresses[0]}:`);
hostnames.forEach(hostname => {
console.log(` ${hostname}`);
});
});
});
Run example »
DNS Record Types
The DNS module supports lookups for various DNS record types:
Method | Record Type | Description |
---|---|---|
resolve4() |
A | IPv4 addresses |
resolve6() |
AAAA | IPv6 addresses |
resolveMx() |
MX | Mail exchange records |
resolveTxt() |
TXT | Text records |
resolveSrv() |
SRV | Service records |
resolveNs() |
NS | Name server records |
resolveCname() |
CNAME | Canonical name records |
resolveSoa() |
SOA | Start of authority records |
resolvePtr() |
PTR | Pointer records |
resolveNaptr() |
NAPTR | Name authority pointer records |
resolveAny() |
ANY | Any records |
Advanced DNS Operations
1. Custom DNS Resolution
Create a custom DNS resolver with specific settings for more control over DNS lookups:
const dns = require('dns');
// Create a new resolver
const resolver = new dns.Resolver();
// Set custom server (Google's public DNS)
resolver.setServers(['8.8.8.8', '8.8.4.4']);
// Use the custom resolver
resolver.resolve4('www.example.com', (err, addresses) => {
if (err) throw err;
console.log('Addresses resolved using Google DNS:');
addresses.forEach(addr => {
console.log(` ${addr}`);
});
});
// See what servers are configured
console.log('Current resolver servers:', resolver.getServers());
Run example »
Note: Creating a custom resolver is useful when you want to use specific DNS servers instead of the system's defaults, or when you need different settings for different lookups.
2. Network vs. Operating System Level Resolution
The DNS module offers two different approaches to name resolution:
Function | Implementation | Network Calls | Uses |
---|---|---|---|
dns.lookup() |
Uses getaddrinfo() system call |
No direct network calls | Follows local configuration (hosts file, etc.) |
dns.resolve*(), dns.reverse() |
Makes actual network requests | Always connects to DNS servers | Bypasses local configuration, direct DNS queries |
Warning: Due to these differences, the results from dns.lookup()
and dns.resolve*()
methods may not always match, especially in environments with custom host configurations.
3. Error Handling and Retries
Robust DNS handling requires proper error management. Here's how to handle common DNS errors and implement retry logic:
const dns = require('dns');
function lookupWithErrorHandling(domain) {
dns.lookup(domain, (err, address, family) => {
if (err) {
console.error(`DNS lookup failed for ${domain}`);
// Check specific error codes
switch (err.code) {
case 'ENOTFOUND':
console.error(' Domain name not found');
break;
case 'ETIMEDOUT':
console.error(' DNS lookup timed out');
break;
case 'ENODATA':
console.error(' Domain exists but no data of requested type');
break;
case 'ESERVFAIL':
console.error(' DNS server returned general failure');
break;
default:
console.error(` Error code: ${err.code}`);
}
return;
}
console.log(`DNS lookup successful for ${domain}`);
console.log(` IP address: ${address}`);
console.log(` IP version: IPv${family}`);
});
}
// Test with valid and invalid domains
lookupWithErrorHandling('www.google.com');
lookupWithErrorHandling('this-domain-does-not-exist-123456789.com');
Run example »
Note: DNS errors can be temporary due to network issues or DNS propagation delays.
In production applications, you might want to implement retry logic with exponential backoff.
Performance Optimization
DNS lookups can be a performance bottleneck in applications. Here are strategies to optimize DNS resolution:
1. Caching
Implement a simple DNS cache to avoid repeated lookups for the same domain:
const dns = require('dns');
const util = require('util');
const lookup = util.promisify(dns.lookup);
const dnsCache = new Map();
async function cachedLookup(domain) {
if (dnsCache.has(domain)) {
console.log('Cache hit for:', domain);
return dnsCache.get(domain);
}
console.log('Cache miss for:', domain);
const result = await lookup(domain);
dnsCache.set(domain, result);
return result;
}
// Example usage
(async () => {
const domains = ['google.com', 'facebook.com', 'google.com'];
for (const domain of domains) {
const result = await cachedLookup(domain);
console.log(`${domain} → ${result.address}`);
}
})();
Run example »
2. Parallel Lookups
Use Promise.all()
to perform multiple DNS lookups in parallel:
const dns = require('dns').promises;
async function lookupMultiple(domains) {
try {
const lookups = domains.map(domain => dns.lookup(domain));
const results = await Promise.all(lookups);
return domains.map((domain, i) => ({
domain,
...results[i]
}));
} catch (err) {
console.error('One or more lookups failed:', err);
throw err;
}
}
// Example usage
lookupMultiple(['google.com', 'facebook.com', 'github.com'])
.then(results => console.log(results))
.catch(console.error);
Run example »
3. Custom Resolvers and Timeouts
Configure custom DNS servers and timeouts for better control:
const dns = require('dns');
const { Resolver } = dns;
// Create a custom resolver with timeout
const resolver = new Resolver();
resolver.setServers(['8.8.8.8', '1.1.1.1']); // Google and Cloudflare DNS
// Set timeout for all operations (in ms)
const TIMEOUT = 2000;
async function resolveWithTimeout(domain, rrtype = 'A') {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`DNS query timed out after ${TIMEOUT}ms`));
}, TIMEOUT);
resolver.resolve(domain, rrtype, (err, addresses) => {
clearTimeout(timer);
if (err) return reject(err);
resolve(addresses);
});
});
}
// Example usage
resolveWithTimeout('example.com')
.then(console.log)
.catch(console.error);
Run example »
DNS Module vs. Third-Party DNS Libraries
Feature | Node.js DNS Module | Third-Party Libraries |
---|---|---|
Installation | Built-in, no dependencies | Requires installation and management |
Feature set | Basic DNS operations | Often more comprehensive |
Caching | Not built-in | Often includes caching |
Advanced features | Limited | May include DNSSEC, DoH, DoT support |
Performance | Good for basic use | May be optimized for specific use cases |
Popular third-party DNS libraries for Node.js include:
dns-packet
: Low-level DNS packet encoding/decodingnative-dns
: More complete DNS implementationdns2
: Modern DNS library with promise support
Summary
The Node.js DNS module provides essential functionality for interacting with the Domain Name System. Key features include:
- Looking up IP addresses for domain names
- Resolving various DNS record types (A, AAAA, MX, TXT, etc.)
- Performing reverse DNS lookups
- Creating custom resolvers with specific settings
- Both callback-based and promise-based APIs
Understanding the DNS module is crucial for applications that need to interact with network resources by domain name, implement custom name resolution logic, or verify domain-related information.