Cómo validar correos electrónicos en Node.js sin instalar un paquete
Tres llamadas API verifican la sintaxis, los registros MX y los dominios desechables. Sin instalación npm, sin archivos regex, sin tiempo de espera SMTP. Funciona desde fetch en cualquier versión de Node.js.
Formato de capturas de expresiones regulares. Se echa de menos todo lo demás.
user@fakdomain123.com pasa. user@mailinator.com pasa.
admin@company.com (una dirección basada en roles que debes marcar) pasa.
Cada expresión regular que copias de Stack Overflow tiene el mismo punto ciego: valida caracteres, no infraestructura.
Los paquetes npm tampoco resuelven esto. email-validator agrega una dependencia y aún solo verifica el formato.
deep-email-validator realiza un sondeo SMTP, que se agota detrás de los firewalls y hace que los proveedores de correo incluyan la IP de su servidor en la lista bloqueada.
Necesita tres comprobaciones: sintaxis, registros MX y detección de proveedores desechables. La API de botoi hace las tres cosas en una POST. Sin instalación. Sin archivo de expresiones regulares. Sin conexión SMTP.
La llamada API
Un comando curl para ver la forma de respuesta:
curl -X POST https://api.botoi.com/v1/email/validate \\
-H "Content-Type: application/json" \\
-d '{"email": "test@tempmail.xyz"}'
La respuesta te dice todo sobre la dirección:
{
"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 la sintaxis. is_valid combina formato, MX y capacidad de entrega en un solo booleano.
is_disposable proveedores de banderas desechables. is_role captura direcciones como admin@ y info@.
reason le dice por qué falló la validación cuando is_valid es falso.
Integración de Node.js: ruta de registro exprés
Aquí hay un controlador de ruta Express completo que valida el correo electrónico en POST /signup utilizando la recuperación nativa.
El patrón de apertura fallida garantiza que una interrupción del botoi nunca bloquee los registros reales:
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);
La ruta rechaza correos electrónicos no válidos con un 422 y el específico reason desde la API.
Si no se puede acceder a la API, se continúa con el registro. Cambia un cheque perdido por un flujo de usuarios ininterrumpido.
Three-layer validation
La /v1/email/validate El punto final cubre los conceptos básicos. Para una verificación exhaustiva, combine tres puntos finales:
/v1/email/validatepara sintaxis y formato/v1/email-mx/verifypara verificación de registros MX/v1/disposable-email/checkpara detección desechable
Esta función auxiliar llama a los tres y devuelve un resultado estructurado:
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;
}
Úselo así:
const result = await validateEmail('user@tempmail.xyz');
if (!result.valid) {
console.log(\`Rejected: \${result.reason}\`);
// Rejected: disposable_provider
}
Cada capa detecta una clase diferente de correo electrónico incorrecto. Las comprobaciones de formato detienen la entrada de basura. Los controles MX detienen los dominios inventados. Los cheques desechables evitan los registros desechables. Juntos, cubren el espacio que las expresiones regulares dejan abierto.
Agregar a una ruta API Next.js
La misma lógica funciona en un controlador de ruta de Next.js App Router en app/api/validate-email/route.ts.
Esta versión ejecuta la verificación de formato y la verificación desechable en paralelo para reducir la latencia:
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 });
}
}
Llame a esta ruta desde su formulario de registro onBlur controlador para validar el correo electrónico antes de que el usuario lo envíe.
el paralelo Promise.all mantiene el tiempo de respuesta por debajo de 200 ms para la mayoría de las solicitudes.
Manejo de casos extremos
Tres patrones hacen tropezar a los validadores ingenuos:
Dominios generales
Algunos dominios aceptan correo electrónico en cualquier dirección. anything@catch-all-domain.com no rebotará,
pero es posible que el buzón no exista. La verificación MX confirma que el dominio tiene un servidor de correo.
Para confirmar el buzón específico es necesario enviar un correo electrónico, lo que este método evita intencionadamente.
Para la mayoría de los flujos de registro, la verificación a nivel de dominio es suficiente.
Direcciones basadas en roles
Direcciones como admin@, info@, y support@ son bandejas de entrada compartidas.
Son válidos, pero no son buenos candidatos para ser propietarios de una cuenta porque ninguna persona los controla.
La API los marca con is_role: true. Puede advertir al usuario o bloquear el registro según las necesidades de su producto.
Más direccionamiento
user+tag@gmail.com entrega a user@gmail.com.
Esta es una característica legítima, no un abuso. Pero permite que una persona cree muchas cuentas con el mismo buzón.
Si quiere evitar esto, quite el + sufijo antes de la validación y almacenar la dirección normalizada.
Aquí hay una función que maneja los tres:
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
// }
Paquetes npm versus enfoque API
| Característica | validador de correo electrónico | validador de correo electrónico profundo | botoi API |
|---|---|---|---|
| Dependencias | 1 | 5+ | 0 (búsqueda nativa) |
| Verificación de formato | Sí | Sí | Sí |
| cheque MX | No | Sí (SMTP) | Sí (DNS) |
| Detección desechable | No | Sí (lista local) | Sí (más de 700 dominios) |
| Sondeo SMTP | No | Sí (poco confiable) | No (basado en DNS) |
| Seguro cortafuegos | Sí | No | Sí |
| Mantenimiento | tu actualizas | tu actualizas | Actualizaciones de API |
El enfoque del paquete npm coloca la lógica de validación y sus datos (listas de dominios, patrones de expresiones regulares) en su código base. Eres dueño de las actualizaciones. Cuando se lanza un nuevo proveedor desechable, alguien debe abrir un PR para agregarlo. El enfoque API descarga ese mantenimiento. La lista de dominios se actualiza en el lado del servidor. Tu código sigue siendo el mismo.
La compensación: una API agrega una dependencia de la red. El patrón de apertura fallida que se muestra en los ejemplos de Express y Next.js maneja esto. Si la API no funciona, la validación pasa y usted confía en sus otras protecciones de registro (confirmación por correo electrónico, limitación de velocidad) hasta que la API se recupere.
FAQ
- ¿Cómo valido una dirección de correo electrónico en Node.js sin instalar un paquete?
- Utilice la API de recuperación nativa para llamar a un punto final de validación como la API de correo electrónico de botoi. Envíe una solicitud POST con la dirección de correo electrónico y la API devuelve la validez del formato, el estado del registro MX y la detección de proveedores desechables. No requiere instalación de npm, mantenimiento de expresiones regulares ni conexiones SMTP.
- ¿Cuál es la mejor API de validación de correo electrónico para Node.js?
- La mejor API combina tres comprobaciones en una sola llamada: validación de sintaxis, verificación de registros MX y detección de dominios desechables. La API de botoi cubre los tres y devuelve resultados en menos de 100 ms. Funciona desde cualquier versión de Node.js que admita fetch (18+) y el nivel gratuito maneja 100 solicitudes por día.
- ¿Cómo verifico si existe una dirección de correo electrónico sin enviar un mensaje?
- Puede verificar que el dominio tenga registros MX válidos mediante una verificación basada en DNS, que confirma que el servidor de correo existe y acepta conexiones. El punto final botoi /v1/email-mx/verify hace esto a través de DNS sin abrir una conexión SMTP, por lo que su IP nunca aparece en la lista bloqueada. Este enfoque confirma que el dominio es real pero no puede confirmar si existe un buzón de correo específico.
- ¿Cómo detecto direcciones de correo electrónico desechables en Node.js?
- Llame al punto final botoi /v1/disposable-email/check con la dirección de correo electrónico. Comprueba más de 700 proveedores desechables conocidos y devuelve un booleano is_disposable. Puede combinar esto con la validación de formato y la verificación MX para obtener un proceso de validación exhaustivo, todo mediante llamadas de recuperación nativas.
- ¿Es suficiente la expresión regular para la validación del correo electrónico en Node.js?
- No. Regex solo verifica el formato. Un correo electrónico como usuario@fakdomain123.com pasa expresiones regulares pero no tiene servidor de correo. user@mailinator.com pasa expresiones regulares pero es una dirección desechable. La validación efectiva requiere verificar los registros MX y examinar los proveedores desechables, lo que las expresiones regulares no pueden hacer.
Empieza a construir con botoi
150+ endpoints de API para consultas, procesamiento de texto, generacion de imagenes y utilidades para desarrolladores. Plan gratuito, sin tarjeta de credito.