Como validar emails em Node.js sem instalar um pacote
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.
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/validatepara sintaxe e formato/v1/email-mx/verifypara verificação de registro MX/v1/disposable-email/checkpara 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.