Pular para o conteúdo
Integration

Como validar emails em Node.js sem instalar um pacote

| 7 min read

Três chamadas de API verificam sintaxe, registros MX e domínios descartáveis. Sem instalação npm, sem arquivo regex, sem tempo limite de SMTP. Funciona a partir de busca em qualquer versão do Node.js.

Email validation interface on a laptop screen
Photo by Stephen Phillips on Unsplash

Regex captura formato. Falta todo o resto. user@fakdomain123.com passa. user@mailinator.com passa. admin@company.com (um endereço baseado em função que você deve sinalizar) é aprovado. Cada regex que você copia do Stack Overflow tem o mesmo ponto cego: valida caracteres, não infraestrutura.

Os pacotes npm também não resolvem isso. email-validator adiciona uma dependência e ainda verifica apenas o formato. deep-email-validator faz sondagem SMTP, que atinge o tempo limite atrás de firewalls e coloca o IP do seu servidor na lista de bloqueio dos provedores de e-mail.

Você precisa de três verificações: sintaxe, registros MX e detecção de provedor descartável. A API botoi faz todos os três em um POST. Sem instalação. Nenhum arquivo regex. Sem conexão SMTP.

A chamada da API

Um comando curl para ver o formato da resposta:

curl -X POST https://api.botoi.com/v1/email/validate \\
  -H "Content-Type: application/json" \\
  -d '{"email": "test@tempmail.xyz"}'

A resposta informa tudo sobre o endereço:

{
  "success": true,
  "data": {
    "email": "test@tempmail.xyz",
    "is_valid": false,
    "reason": "no_mx_records",
    "is_free": false,
    "is_role": false,
    "is_disposable": true,
    "domain": "tempmail.xyz",
    "format_valid": true
  }
}

format_valid confirma a sintaxe. is_valid combina formato, MX e capacidade de entrega em um único booleano. is_disposable sinaliza fornecedores descartáveis. is_role captura endereços como admin@ e info@. reason informa por que a validação falhou quando is_valid é falso.

Integração Node.js: rota de inscrição expressa

Aqui está um manipulador de rota Express completo que valida o email no POST /signup usando busca nativa. O padrão de falha aberta garante que uma interrupção do botoi nunca bloqueie inscrições reais:

import express from 'express';

const app = express();
app.use(express.json());

app.post('/signup', async (req, res) => {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.status(400).json({ error: 'Email and password are required' });
  }

  // Validate email before creating the account
  try {
    const validation = await fetch('https://api.botoi.com/v1/email/validate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': \`Bearer \${process.env.BOTOI_API_KEY}\`,
      },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(3000),
    });

    const result = await validation.json();

    if (result.success && !result.data.is_valid) {
      return res.status(422).json({
        error: 'Invalid email address',
        reason: result.data.reason,
      });
    }
  } catch {
    // Fail open: if the API is unreachable, let the signup proceed
    console.warn('Email validation API unreachable, skipping check');
  }

  // Email passed validation; continue with account creation
  // await createUser(email, password);

  return res.status(201).json({ message: 'Account created' });
});

app.listen(3000);

A rota rejeita e-mails inválidos com 422 e o específico reason da API. Se a API estiver inacessível, a inscrição continuará. Você troca um cheque perdido por um fluxo de usuários ininterrupto.

Validação de três camadas

O /v1/email/validate endpoint cobre o básico. Para uma verificação completa, combine três pontos finais:

  • /v1/email/validate para sintaxe e formato
  • /v1/email-mx/verify para verificação de registro MX
  • /v1/disposable-email/check para detecção descartável

Esta função auxiliar chama todos os três e retorna um resultado estruturado:

interface ValidationResult {
  valid: boolean;
  formatValid: boolean;
  mxValid: boolean;
  isDisposable: boolean;
  reason: string | null;
}

const API_BASE = 'https://api.botoi.com/v1';
const headers = {
  'Content-Type': 'application/json',
  'Authorization': \`Bearer \${process.env.BOTOI_API_KEY}\`,
};

async function validateEmail(email: string): Promise<ValidationResult> {
  const result: ValidationResult = {
    valid: true,
    formatValid: false,
    mxValid: false,
    isDisposable: false,
    reason: null,
  };

  // Layer 1: Syntax and format check
  const formatRes = await fetch(\`\${API_BASE}/email/validate\`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email }),
    signal: AbortSignal.timeout(3000),
  });
  const formatData = await formatRes.json();

  if (!formatData.success || !formatData.data.format_valid) {
    return { ...result, valid: false, reason: 'invalid_format' };
  }
  result.formatValid = true;

  // Layer 2: MX record verification
  const mxRes = await fetch(\`\${API_BASE}/email-mx/verify\`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email }),
    signal: AbortSignal.timeout(3000),
  });
  const mxData = await mxRes.json();

  if (!mxData.success || !mxData.data.has_mx) {
    return { ...result, valid: false, reason: 'no_mx_records' };
  }
  result.mxValid = true;

  // Layer 3: Disposable provider detection
  const dispRes = await fetch(\`\${API_BASE}/disposable-email/check\`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email }),
    signal: AbortSignal.timeout(3000),
  });
  const dispData = await dispRes.json();

  if (dispData.success && dispData.data.is_disposable) {
    return { ...result, valid: false, isDisposable: true, reason: 'disposable_provider' };
  }

  return result;
}

Use assim:

const result = await validateEmail('user@tempmail.xyz');

if (!result.valid) {
  console.log(\`Rejected: \${result.reason}\`);
  // Rejected: disposable_provider
}

Cada camada captura uma classe diferente de e-mail incorreto. As verificações de formato interrompem a entrada de lixo. As verificações MX interrompem domínios inventados. Cheques descartáveis ​​impedem inscrições descartáveis. Juntos, eles cobrem a lacuna que a regex deixa aberta.

Adicionando a uma rota da API Next.js

A mesma lógica funciona em um manipulador de rota Next.js App Router em app/api/validate-email/route.ts. Esta versão executa a verificação de formato e a verificação descartável em paralelo para reduzir a latência:

import { NextResponse } from 'next/server';

const API_BASE = 'https://api.botoi.com/v1';
const headers = {
  'Content-Type': 'application/json',
  'Authorization': \`Bearer \${process.env.BOTOI_API_KEY}\`,
};

export async function POST(req: Request) {
  const body = await req.json();
  const email = body.email?.trim().toLowerCase();

  if (!email) {
    return NextResponse.json(
      { error: 'Email is required' },
      { status: 400 }
    );
  }

  try {
    // Run format check and disposable check in parallel
    const [formatRes, dispRes] = await Promise.all([
      fetch(\`\${API_BASE}/email/validate\`, {
        method: 'POST',
        headers,
        body: JSON.stringify({ email }),
        signal: AbortSignal.timeout(3000),
      }),
      fetch(\`\${API_BASE}/disposable-email/check\`, {
        method: 'POST',
        headers,
        body: JSON.stringify({ email }),
        signal: AbortSignal.timeout(3000),
      }),
    ]);

    const [formatData, dispData] = await Promise.all([
      formatRes.json(),
      dispRes.json(),
    ]);

    if (formatData.success && !formatData.data.format_valid) {
      return NextResponse.json(
        { valid: false, reason: 'Invalid email format' },
        { status: 422 }
      );
    }

    if (dispData.success && dispData.data.is_disposable) {
      return NextResponse.json(
        { valid: false, reason: 'Disposable emails are not allowed' },
        { status: 422 }
      );
    }

    // Then check MX records
    const mxRes = await fetch(\`\${API_BASE}/email-mx/verify\`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(3000),
    });
    const mxData = await mxRes.json();

    if (mxData.success && !mxData.data.has_mx) {
      return NextResponse.json(
        { valid: false, reason: 'Email domain has no mail server' },
        { status: 422 }
      );
    }

    return NextResponse.json({ valid: true });
  } catch {
    // Fail open on API errors
    return NextResponse.json({ valid: true });
  }
}

Chame esta rota no seu formulário de inscrição onBlur manipulador para validar o e-mail antes que o usuário o envie. O paralelo Promise.all mantém o tempo de resposta abaixo de 200 ms para a maioria das solicitações.

Lidando com casos extremos

Três padrões atrapalham validadores ingênuos:

Domínios abrangentes

Alguns domínios aceitam email em qualquer endereço. anything@catch-all-domain.com não vai pular, mas a caixa de correio pode não existir. A verificação MX confirma que o domínio possui um servidor de e-mail. A confirmação da caixa de correio específica requer o envio de um e-mail, o que esta abordagem evita propositalmente. Para a maioria dos fluxos de inscrição, a verificação em nível de domínio é suficiente.

Endereços baseados em funções

Endereços como admin@, info@, e support@ são caixas de entrada compartilhadas. Eles são válidos, mas são maus candidatos à propriedade de contas porque nenhuma pessoa os controla. A API sinaliza isso com is_role: true. Você pode avisar o usuário ou bloquear a inscrição com base nas necessidades do seu produto.

Mais endereçamento

user+tag@gmail.com entrega para user@gmail.com. Este é um recurso legítimo, não um abuso. Mas permite que uma pessoa crie várias contas com a mesma caixa de correio. Se você quiser evitar isso, retire o + sufixo antes da validação e armazena o endereço normalizado.

Aqui está uma função que lida com todos os três:

async function validateEmailStrict(email: string): Promise<{
  valid: boolean;
  warnings: string[];
  reason: string | null;
}> {
  const warnings: string[] = [];

  // Check for plus addressing (user+tag@gmail.com)
  const localPart = email.split('@')[0];
  if (localPart.includes('+')) {
    warnings.push('plus_addressing');
  }

  const res = await fetch('https://api.botoi.com/v1/email/validate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': \`Bearer \${process.env.BOTOI_API_KEY}\`,
    },
    body: JSON.stringify({ email }),
    signal: AbortSignal.timeout(3000),
  });

  const data = await res.json();

  if (!data.success) {
    return { valid: false, warnings, reason: 'api_error' };
  }

  // Flag role-based addresses (admin@, info@, support@)
  if (data.data.is_role) {
    warnings.push('role_based_address');
  }

  // Reject disposable providers
  if (data.data.is_disposable) {
    return { valid: false, warnings, reason: 'disposable_provider' };
  }

  // Reject domains with no MX records
  if (!data.data.is_valid && data.data.reason === 'no_mx_records') {
    return { valid: false, warnings, reason: 'no_mx_records' };
  }

  return { valid: data.data.is_valid, warnings, reason: null };
}
const result = await validateEmailStrict('admin+test@example.com');
// {
//   valid: true,
//   warnings: ['plus_addressing', 'role_based_address'],
//   reason: null
// }

pacotes npm vs. abordagem API

Recurso validador de e-mail validador de e-mail profundo API botoi
Dependências 1 5+ 0 (busca nativa)
Verificação de formato Sim Sim Sim
Verificação MX Não Sim (SMTP) Sim (DNS)
Detecção descartável Não Sim (lista local) Sim (mais de 700 domínios)
Sondagem SMTP Não Sim (não confiável) Não (baseado em DNS)
Firewall seguro Sim Não Sim
Manutenção Você atualiza Você atualiza Atualizações de API

A abordagem do pacote npm coloca a lógica de validação e seus dados (listas de domínios, padrões regex) em sua base de código. Você possui as atualizações. Quando um novo fornecedor descartável é lançado, alguém precisa abrir um PR para adicioná-lo. A abordagem da API alivia essa manutenção. A lista de domínios é atualizada no lado do servidor. Seu código permanece o mesmo.

A desvantagem: uma API adiciona uma dependência de rede. O padrão de falha aberta mostrado nos exemplos Express e Next.js lida com isso. Se a API estiver inativa, a validação será aprovada e você confiará em suas outras proteções de inscrição (confirmação por e-mail, limitação de taxa) até que a API se recupere.

FAQ

Como valido um endereço de e-mail em Node.js sem instalar um pacote?
Use a API de busca nativa para chamar um endpoint de validação como a API botoi email. Envie uma solicitação POST com o endereço de e-mail e a API retornará a validade do formato, o status do registro MX e a detecção de provedor descartável. Sem instalação de npm, sem manutenção de regex e sem necessidade de conexões SMTP.
Qual é a melhor API de validação de e-mail para Node.js?
A melhor API combina três verificações em uma chamada: validação de sintaxe, verificação de registro MX e detecção de domínio descartável. A API botoi cobre todos os três e retorna resultados em menos de 100 ms. Funciona em qualquer versão do Node.js que suporte fetch (18+), e o nível gratuito lida com 100 solicitações por dia.
Como posso verificar se existe um endereço de e-mail sem enviar uma mensagem?
Você pode verificar se o domínio possui registros MX válidos usando uma verificação baseada em DNS, que confirma que o servidor de e-mail existe e aceita conexões. O endpoint botoi /v1/email-mx/verify faz isso por DNS sem abrir uma conexão SMTP, para que seu IP nunca fique na lista de bloqueio. Esta abordagem confirma que o domínio é real, mas não pode confirmar se existe uma caixa de correio específica.
Como detecto endereços de e-mail descartáveis ​​em Node.js?
Chame o endpoint botoi /v1/disposable-email/check com o endereço de e-mail. Ele verifica mais de 700 provedores descartáveis ​​conhecidos e retorna um booleano is_disposable. Você pode combinar isso com validação de formato e verificação MX para obter um pipeline de validação completo, tudo usando chamadas de busca nativas.
O regex é suficiente para validação de email no Node.js?
Não. Regex verifica apenas o formato. Um e-mail como user@fakdomain123.com passa por regex, mas não possui servidor de e-mail. user@mailinator.com passa regex, mas é um endereço descartável. A validação eficaz requer a verificação dos registros MX e a triagem de provedores descartáveis, o que a regex não pode fazer.

Comece a construir com botoi

150+ endpoints de API para consultas, processamento de texto, geração de imagens e utilitários para desenvolvedores. Plano gratuito, sem cartão de crédito.