Antes de uma aplicação acessar uma API, banco ou serviço externo pelo nome, esse endereço precisa ser convertido em um IP. O módulo DNS no Node.js, disponível como node:dns, oferece APIs para resolver hosts, consultar registros e controlar servidores DNS usados pela aplicação.
Problemas de DNS podem parecer falhas de rede, timeout ou indisponibilidade do serviço. Além disso, dns.lookup() e dns.resolve() possuem comportamentos diferentes. Escolher a função errada pode afetar cache, ordem IPv4/IPv6 e consumo do thread pool.
Neste guia, você aprenderá a resolver endereços, consultar registros A, AAAA, MX e TXT, usar Promises, definir servidores customizados, aplicar cache e diagnosticar falhas.
Carregando o módulo
const dns = require('node:dns');
const dnsPromises = require('node:dns/promises');A documentação oficial do módulo DNS detalha funções, códigos de erro e a classe Resolver. Para revisar redes e servidores, veja o que é um servidor e o que é Node.js.
dns.lookup() e o resolvedor do sistema
const dns = require('node:dns/promises');
const result = await dns.lookup('example.com');
console.log(result.address, result.family);lookup() usa mecanismos do sistema operacional semelhantes aos usados por outras aplicações. Ele pode considerar arquivo hosts, configuração local e políticas do ambiente. Apesar da interface assíncrona, algumas resoluções usam o thread pool do Node.js.
Retornando todos os endereços
const addresses = await dns.lookup('example.com', {
all: true,
verbatim: true
});Com all, a função retorna IPv4 e IPv6 disponíveis. A opção verbatim preserva a ordem entregue pelo resolvedor. Em versões atuais, também existe configuração de ordem padrão com dns.setDefaultResultOrder().
Consultando registros diretamente
resolve() faz consultas DNS pela rede e não usa exatamente o mesmo caminho de lookup():
const ips = await dns.resolve4('example.com');
const ipv6 = await dns.resolve6('example.com');Essa abordagem é apropriada quando você precisa de registros específicos ou TTL, não apenas de um endereço para abrir conexão.
Consultando TTL
const records = await dns.resolve4('example.com', {
ttl: true
});
for (const record of records) {
console.log(record.address, record.ttl);
}TTL indica por quanto tempo uma resposta pode ser armazenada. Um cache deve respeitar esse valor e também ter limites de tamanho.
Registros MX
const mailServers = await dns.resolveMx('example.com');
mailServers.sort((a, b) => a.priority - b.priority);Registros MX indicam servidores de e-mail. Encontrar um MX não garante que o endereço de e-mail exista ou que mensagens serão aceitas.
Registros TXT
const txt = await dns.resolveTxt('example.com');
const values = txt.map(parts => parts.join(''));
console.log(values);Um registro TXT pode ser dividido em vários fragmentos. SPF, verificações de domínio e outras informações usam esse formato.
Registros SRV
const services = await dns.resolveSrv(
'_service._tcp.example.com'
);SRV fornece host, porta, prioridade e peso. Clientes precisam aplicar corretamente prioridade e distribuição por peso, não apenas escolher o primeiro elemento.
Resolução reversa
const names = await dns.reverse('8.8.8.8');Uma resposta reversa não prova identidade ou propriedade. Não use PTR como mecanismo isolado de autenticação.
Classe Resolver
Uma instância separada permite configurar servidores sem alterar o processo inteiro:
const { Resolver } = require('node:dns/promises');
const resolver = new Resolver();
resolver.setServers([
'1.1.1.1',
'8.8.8.8'
]);
const records = await resolver.resolve4('example.com');Isso é útil em ferramentas de diagnóstico ou ambientes com resolvedor dedicado. Servidores públicos podem não conhecer domínios internos.
Timeout e tentativas
Algumas opções do Resolver permitem definir timeout e número de tentativas:
const resolver = new Resolver({
timeout: 3000,
tries: 2
});Retries devem ser limitados e considerar o tempo total da requisição. Para uma política mais ampla, veja o guia de AbortController no Node.js.
Erros comuns de DNS
Trate códigos específicos:
try {
await dns.resolve4('missing.example');
} catch (error) {
if (error.code === 'ENOTFOUND') {
console.log('Domínio não encontrado');
} else if (error.code === 'ETIMEOUT') {
console.log('Servidor DNS não respondeu');
} else {
throw error;
}
}Também podem aparecer ESERVFAIL, EREFUSED, ENODATA e outros. Não transforme todos em “host inexistente”.
Cache na aplicação
Repetir consultas em alto volume aumenta latência e carga. Um cache simples precisa armazenar valor e expiração:
const cache = new Map();
async function resolveCached(hostname) {
const current = cache.get(hostname);
if (current && current.expiresAt > Date.now()) {
return current.addresses;
}
const records = await dns.resolve4(hostname, { ttl: true });
const minimumTtl = Math.min(...records.map(item => item.ttl));
const addresses = records.map(item => item.address);
cache.set(hostname, {
addresses,
expiresAt: Date.now() + minimumTtl * 1000
});
return addresses;
}Limite a quantidade de entradas, trate TTL zero e não mantenha resultados negativos indefinidamente.
DNS e balanceamento
Um hostname pode retornar vários IPs. Se a biblioteca resolve uma vez e mantém a conexão por muito tempo, mudanças no DNS só serão percebidas após reconexão. Considere keep-alive, TTL e política de renovação.
IPv4 e IPv6
Ambientes com IPv6 parcialmente configurado podem apresentar atrasos ou falhas. Não force IPv4 sem diagnosticar. Teste conectividade para ambas as famílias e observe a ordem das respostas.
DNS e segurança
Entradas DNS podem mudar. Ao buscar URLs fornecidas pelo usuário, validar apenas o hostname antes da resolução não impede SSRF. O domínio pode resolver para IP privado ou mudar entre verificações. Valide endereços finais e controle redirecionamentos.
O artigo sobre segurança em aplicações web apresenta outros controles.
Não bloqueie o thread pool
Muitas chamadas de lookup() concorrentes podem competir com operações que usam o thread pool. Meça filas e latência. Consultas com resolve* usam a implementação de DNS do Node.js e possuem características diferentes.
Observabilidade
Registre hostname lógico, tipo de consulta, duração, código de erro, servidor usado e quantidade de respostas. Evite registrar domínios contendo dados pessoais ou tokens. Monitore aumento de timeout, SERVFAIL e respostas vazias.
Testando
Não dependa apenas do DNS público nos testes. Injete uma função de resolução ou use um servidor controlado. Teste múltiplos IPs, TTL curto, NXDOMAIN, timeout, IPv6 e alteração de registros.
Erros comuns
- Confundir lookup e resolve: resultados e caminho de resolução diferem.
- Ignorar TTL: cache mantém IP antigo por tempo demais.
- Não limitar cache: hostnames arbitrários consomem memória.
- Tratar todo erro como ENOTFOUND: diagnóstico fica incorreto.
- Escolher sempre o primeiro SRV: prioridade e peso são ignorados.
- Confiar em PTR para autenticação: o registro não prova identidade.
- Validar hostname mas não IP final: SSRF continua possível.
- Forçar IPv4 sem medir: compatibilidade futura é reduzida.
Boas práticas para produção
- Escolha conscientemente entre
lookup()eresolve(). - Respeite TTL e limite caches.
- Aplique timeout e tentativas limitadas.
- Trate códigos de erro separadamente.
- Teste IPv4 e IPv6.
- Valide IP final em operações sensíveis.
- Monitore latência e falhas do resolvedor.
- Use Resolver separado quando precisar de servidores próprios.
- Considere reconexão após mudanças de DNS.
- Não use DNS como autenticação.
Conclusão
O DNS no Node.js oferece funções para resolver hosts e consultar registros específicos. lookup() integra-se ao resolvedor do sistema, enquanto resolve* permite consultas DNS detalhadas.
Uma implementação confiável combina timeout, cache com TTL, tratamento de erros e suporte a múltiplos endereços. Com observabilidade e validação de segurança, a resolução de nomes deixa de ser uma dependência invisível e passa a ser uma parte controlada da aplicação.




