DNS Lookups (dns Module)
Before any socket can connect, a hostname like example.com must be turned into an IP address like 93.184.216.34. That translation is DNS (the Domain Name System), and Node's dns module exposes it. There's an important and frequently-misunderstood split here: dns.lookup (which uses the OS resolver) versus the dns.resolve* family (which queries DNS servers directly). Choosing wrong has real performance consequences.
The promise API
import { lookup, resolve4, resolveMx } from 'node:dns/promises'
// Hostname → IP, the way connect() does it:
const { address, family } = await lookup('example.com')
console.log(address, 'IPv' + family) // e.g. 93.184.216.34 IPv4
// Query specific record types directly from DNS:
console.log(await resolve4('example.com')) // ['93.184.216.34', ...] all A records
console.log(await resolveMx('gmail.com')) // mail servers with prioritieslookup vs resolve — the key distinction
|
| |
|---|---|---|
Mechanism | OS resolver (getaddrinfo) | Direct DNS query (c-ares) |
Honors | Yes | No |
Honors OS cache & config | Yes | No |
Record types | A/AAAA only (IP for connecting) | A, AAAA, MX, TXT, SRV, NS, CNAME… |
Runs on | libuv threadpool (blocking call) | Async network I/O (non-blocking) |
Use for | Getting an IP to connect to | Inspecting DNS records |
Querying specific record types
import { resolve4, resolve6, resolveMx, resolveTxt, resolveCname } from 'node:dns/promises'
await resolve4('example.com') // IPv4 A records → ['93.184.216.34']
await resolve6('example.com') // IPv6 AAAA records
await resolveMx('example.com') // [{ priority: 10, exchange: 'mail.example.com' }]
await resolveTxt('example.com') // TXT records (SPF, verification tokens)
await resolveCname('www.example.com') // canonical name aliasesRecord | Holds | Method |
|---|---|---|
A / AAAA | IPv4 / IPv6 address |
|
MX | Mail servers (+ priority) |
|
TXT | Arbitrary text (SPF, DKIM, verification) |
|
CNAME | Alias to another name |
|
SRV | Service host + port |
|
NS | Authoritative name servers |
|
Reverse DNS
import { reverse } from 'node:dns/promises'
const hostnames = await reverse('93.184.216.34') // IP → ['example.com', ...]
console.log(hostnames)Controlling resolution
import { setServers, getServers, Resolver } from 'node:dns/promises'
console.log(await getServers()) // current DNS servers
setServers(['8.8.8.8', '1.1.1.1']) // override globally
// Or an isolated resolver with its own servers/timeout:
const resolver = new Resolver({ timeout: 2000, tries: 2 })
resolver.setServers(['1.1.1.1'])
console.log(await resolver.resolve4('example.com'))Common errors
Code | Meaning |
|---|---|
| Hostname does not exist (NXDOMAIN) or no record |
| DNS server failed to complete the query |
| Query timed out (server unreachable/slow) |
| Name exists but has no record of the requested type |
Practical guidance
For "I just need an IP to connect to," let the defaults work —
http/netcalllookupfor you.To inspect records (MX, TXT, SRV) or avoid the threadpool, use
resolve*.Under high connection volume, cache DNS results or prefer
resolve*to keep the threadpool free.Never trust reverse DNS for authentication.