EmailCheckerEmailChecker

Validar email em Node.js — 4 abordagens com código

Regex no client, lib npm, DNS MX lookup nativo e API REST: compare as 4 abordagens de validação de email em Node.js com código real e saiba qual usar em cada cenário.

Por João Costa (Engenharia)·Publicado em 22/05/2026·8 min de leitura

Validar email em Node.js — 4 abordagens com código

Validar endereços de email é um dos problemas mais recorrentes no desenvolvimento web, e também um dos que mais geram dúvida: usar regex? Uma lib? Checar o DNS? Chamar uma API externa? A resposta depende de onde você está na stack e de quanto você precisa confiar naquele dado.

Este artigo apresenta as quatro abordagens principais, com código real para cada uma, e uma matriz de trade-offs no final para ajudar na decisão.


Quando usar cada abordagem

Antes de ver código, uma visão rápida do espaço de soluções:

Abordagem Latência Custo Accuracy Melhor para
Regex simples < 1 ms zero baixa Feedback imediato no client
email-validator (npm) < 1 ms zero media Sanitizacao no backend sem I/O
DNS MX lookup 50-300 ms zero media-alta Verificar se dominio recebe email
API EmailChecker 200-800 ms por requisicao alta Cadastros, leads, limpeza de lista

A regra geral: quanto mais para a direita na tabela, mais tarde no fluxo voce aplica — e mais voce pode confiar no resultado. Nenhuma abordagem substitui a outra; na pratica voce as combina em camadas.


Abordagem 1: Regex simples no client

A validacao por expressao regular e a mais rapida de implementar e roda no navegador sem nenhuma dependencia. Ela verifica apenas a estrutura sintatica do email — nao se o dominio existe ou se a caixa postal aceita mensagens.

Pros:

  • Zero latencia, funciona offline
  • Nao precisa de nenhuma lib ou chamada de rede
  • Boa para feedback visual em tempo real no formulario

Contras:

  • Falsos negativos: rejeita emails validos com caracteres especiais permitidos pelo RFC 5321
  • Falsos positivos: aceita foo@bar.c ou x@y.z que estruturalmente parecem certos mas nunca vao entregar
  • Nao detecta dominios inexistentes ou descontinuados
// Regex pragmatica — cobre 99% dos emails reais sem ser paranoia RFC completa
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;

function isEmailFormatValid(email) {
  if (typeof email !== 'string') return false;
  const trimmed = email.trim().toLowerCase();
  return EMAIL_REGEX.test(trimmed);
}

// Exemplos
console.log(isEmailFormatValid('joao@empresa.com.br')); // true
console.log(isEmailFormatValid('invalido@'));           // false
console.log(isEmailFormatValid('sem-arroba.com'));      // false
console.log(isEmailFormatValid('  espacos@ok.com  ')); // true (trim aplicado)

Use essa abordagem para o feedback inline do campo de formulario. Nao use como unica validacao em dados que vao persistir no banco.


Abordagem 2: email-validator (npm)

A lib email-validator encapsula uma regex mais robusta que cobre melhor o espaco de emails validos segundo o RFC, alem de normalizar o input antes de testar. E uma dependencia minuscula (sem sub-dependencias) e roda tanto no Node quanto no browser via bundler.

Pros:

  • Regex calibrada contra casos de borda do RFC
  • API de uma linha, sem configuracao
  • Adequada para validacao no backend antes de persistir

Contras:

  • Ainda e so verificacao sintatica — nao valida se o dominio existe
  • Adiciona uma dependencia (pequena, mas e uma dependencia)
npm install email-validator
import * as EmailValidator from 'email-validator';
// ou: const EmailValidator = require('email-validator');

const emails = [
  'usuario@dominio.com.br',
  'nome+tag@empresa.io',
  'invalido@',
  '@semlocal.com',
  'a@b.c',           // passa — estruturalmente valido pelo RFC
  'foo bar@test.com' // falha — espaco nao permitido
];

for (const email of emails) {
  const valid = EmailValidator.validate(email);
  console.log(`${email.padEnd(30)} -> ${valid ? 'valido' : 'invalido'}`);
}

Na pratica, use email-validator como segunda camada no servidor para rejeitar entradas claramente erradas antes de qualquer operacao de I/O. Isso reduz carga nas abordagens mais custosas.


Abordagem 3: DNS MX lookup com node:dns/promises

Verificar se o dominio do email possui registros MX (Mail Exchanger) e a forma mais proxima de "validacao real" que voce consegue fazer sem enviar um email de fato. Se nao ha registro MX, nao ha servidor configurado para receber mensagens — o email vai rejeitar independente de qualquer outra coisa.

Essa abordagem usa o modulo nativo node:dns/promises — sem instalar nada.

Vantagens:

  • Descobre dominios inexistentes ou sem MX configurado (ex.: foo@google123xzy.com)
  • Zero custo, zero dependencia externa
  • Roda no servidor com latencia aceitavel (tipicamente 50-300 ms com cache DNS local)

Limitacoes:

  • Nao garante que a caixa especifica existe (joao@ pode ter MX mas rejeitar locais desconhecidos)
  • Alguns dominios corporativos bloqueiam resolucao publica de MX (raro, mas acontece)
  • Nao detecta enderecos temporarios (mailinator, guerrillamail, etc.)
import dns from 'node:dns/promises';

async function hasMxRecord(email) {
  const domain = email.split('@')[1];
  if (!domain) return false;

  try {
    const records = await dns.resolveMx(domain);
    // resolveMx retorna array de { exchange, priority }
    return records.length > 0;
  } catch (err) {
    // ENOTFOUND = dominio nao existe; ENODATA = sem registro MX
    if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') {
      return false;
    }
    // Outros erros (timeout, SERVFAIL) — politica sua: fail-open ou fail-closed
    throw err;
  }
}

// Uso
const tests = [
  'joao@gmail.com',
  'contato@empresa-real.com.br',
  'ninguem@dominioquenoexiste99.xyz',
];

for (const email of tests) {
  const ok = await hasMxRecord(email);
  console.log(`${email}: MX ${ok ? 'encontrado' : 'ausente ou dominio inexistente'}`);
}

Uma nota importante sobre politica de erro: se o DNS retornar timeout ou SERVFAIL, voce precisa decidir se falha fechado (rejeita o email) ou aberto (aceita e continua). Para signup de produto, fail-open e geralmente melhor — voce nao quer bloquear um usuario legitimo por instabilidade de DNS.


Abordagem 4: EmailChecker API

As tres abordagens anteriores sao "gratuitas" mas tem teto de accuracy. Elas nao conseguem saber se a caixa postal especifica aceita mensagens, se o dominio e descartavel (mailinator, yopmail, etc.) ou se o email esta em listas de bounces conhecidos.

A EmailChecker API resolve isso com uma verificacao full-stack: checa sintaxe, dominio, registros MX, conexao SMTP simulada e classifica o risco do endereço. O resultado e um score estruturado que voce usa para tomar decisao de negocio.

const EMAILCHECKER_KEY = process.env.EMAILCHECKER_API_KEY;
const BASE_URL = 'https://api.emailchecker.com.br/v1';

async function validateWithEmailChecker(email) {
  const url = `${BASE_URL}/validate?email=${encodeURIComponent(email)}`;

  const res = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${EMAILCHECKER_KEY}`,
      'Content-Type': 'application/json',
    },
  });

  if (!res.ok) {
    const body = await res.text();
    throw new Error(`EmailChecker API error ${res.status}: ${body}`);
  }

  const data = await res.json();

  // Estrutura do retorno:
  // {
  //   email: string,
  //   valid: boolean,
  //   score: number,          // 0-100, quanto maior mais confiavel
  //   disposable: boolean,    // email descartavel?
  //   mx: boolean,            // tem registro MX?
  //   smtp_check: boolean,    // caixa respondeu ao SMTP probe?
  //   reason: string          // 'valid' | 'invalid_mx' | 'smtp_rejected' | ...
  // }
  return data;
}

// Exemplo de uso em handler de signup
async function handleSignup(req, res) {
  const { email, name } = req.body;

  // Camada 1: regex rapida — rejeita lixo antes de qualquer I/O
  if (!EMAIL_REGEX.test(email)) {
    return res.status(400).json({ error: 'Formato de email invalido' });
  }

  // Camada 2: verificacao completa via API
  const result = await validateWithEmailChecker(email);

  if (!result.valid || result.score < 60) {
    return res.status(422).json({
      error: 'Email nao parece valido ou esta temporariamente indisponivel',
      reason: result.reason,
    });
  }

  if (result.disposable) {
    return res.status(422).json({ error: 'Emails temporarios nao sao aceitos' });
  }

  // Prossegue com o cadastro...
  await createUser({ email, name });
  return res.status(201).json({ ok: true });
}

Para mais exemplos incluindo validacao em batch e webhooks de resultado assincrono, veja a documentacao de exemplos Node. A arquitetura completa da API esta no guia de validacao via API.


Tabela de trade-offs final

Criterio Regex email-validator DNS MX EmailChecker API
Latencia < 1 ms < 1 ms 50-300 ms 200-800 ms
Custo por verificacao zero zero zero pago
Detecta dominio invalido nao nao sim sim
Detecta email descartavel nao nao nao sim
Detecta caixa inexistente nao nao parcial sim
Funciona no browser sim sim (bundlado) nao sim (fetch)
Dependencia externa nao npm nao API REST
Adequado para tempo real sim sim borderline nao

Padrao hibrido recomendado

Para a maioria dos produtos — especialmente fluxos de signup ou captura de leads — o padrao que mais equilibra UX e qualidade de dados e o seguinte:

No client (tempo real, enquanto o usuario digita): regex simples para feedback visual imediato. Nenhuma chamada de rede, nenhuma latencia perceptivel.

No servidor (ao submeter o formulario): DNS MX lookup nativo. Descarta emails com dominio inexistente ou sem servidor de email antes de persistir qualquer coisa. Latencia aceitavel (< 300 ms) e custo zero.

Em background (apos criar o registro): chamada assincrona a EmailChecker API. Voce nao bloqueia o usuario, mas obtem o score completo em segundos. Se o resultado for negativo, pode marcar o registro para revisao ou disparar um email de confirmacao como segundo fator.

// Signup handler com padrao hibrido
async function signup(req, res) {
  const { email, name } = req.body;

  // Camada 1 — sintatica (sem I/O)
  if (!isEmailFormatValid(email)) {
    return res.status(400).json({ error: 'Formato invalido' });
  }

  // Camada 2 — DNS MX (I/O rapido, sem custo)
  const mxOk = await hasMxRecord(email);
  if (!mxOk) {
    return res.status(422).json({ error: 'Dominio nao aceita emails' });
  }

  // Cria o usuario com status pendente
  const user = await createUser({ email, name, emailStatus: 'pending' });

  // Camada 3 — verificacao completa em background (nao bloqueia resposta)
  setImmediate(async () => {
    try {
      const result = await validateWithEmailChecker(email);
      const status = result.valid && result.score >= 60 ? 'verified' : 'suspicious';
      await updateUserEmailStatus(user.id, status, result);
    } catch (err) {
      console.error('EmailChecker background check failed:', err);
      // fail-open: nao penaliza o usuario por erro de API
    }
  });

  return res.status(201).json({ ok: true, userId: user.id });
}

Esse padrao garante que o usuario nunca espera mais do que o DNS MX lookup (tipicamente invisivel dentro do tempo de submit do formulario), enquanto voce acumula dados de qualidade para higienizacao posterior.


Conclusao

Nao existe uma bala de prata para validacao de email. Cada abordagem resolve uma parte diferente do problema:

  • Regex e UX, nao validacao real.
  • email-validator e sanitizacao de backend, nao verificacao de existencia.
  • DNS MX e a primeira verificacao de infraestrutura, rapida e gratuita.
  • EmailChecker API e a camada de confianca — necessaria quando a qualidade do dado tem impacto direto no seu negocio.

Combine as camadas de acordo com o seu contexto. Para um campo de contato simples, regex ja resolve. Para uma plataforma de email marketing onde cada cadastro custa caro, o padrao hibrido com verificacao assincrona e o padrao certo.

Para entender os conceitos por tras da validacao de email antes de implementar, leia o guia conceitual de validacao de email.

Comece agora

Pronto pra parar de mandar email pra endereço morto?

Comece grátis com 500 créditos. Sem cartão, sem compromisso.