How to Validate Email Addresses in Node.js (2026 Guide)

Updated February 2026 · 8 min read

Email validation is one of those problems that looks simple but isn't. A basic regex catches obvious typos, but it won't tell you if the domain has a mail server, if the address is a disposable throwaway, or if the mailbox actually exists.

This guide covers 4 approaches to email validation in Node.js, from basic regex to full API-based verification, with working code for each.

1. Regex Validation (Basic)

The simplest approach. Catches formatting errors but nothing else.

function isValidEmail(email) {
  const regex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
  return regex.test(email) && email.length <= 254;
}

console.log(isValidEmail("user@gmail.com"));     // true
console.log(isValidEmail("not-an-email"));        // false
console.log(isValidEmail("user@.com"));           // false

Pros: Fast, no dependencies, no network calls.
Cons: user@fakdomain123.com passes validation. So does user@mailinator.com. You're only checking format, not deliverability.

2. MX Record Lookup (Medium)

Check if the domain actually has mail servers configured. This catches fake domains that pass regex.

const dns = require('dns').promises;

async function hasMxRecords(domain) {
  try {
    const records = await dns.resolveMx(domain);
    return records && records.length > 0;
  } catch (err) {
    return false;
  }
}

async function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!regex.test(email)) return { valid: false, reason: "Invalid format" };

  const domain = email.split("@")[1];
  const hasMx = await hasMxRecords(domain);

  if (!hasMx) return { valid: false, reason: "Domain has no mail server" };

  return { valid: true };
}

// Usage
const result = await validateEmail("user@fakdomain123.com");
// { valid: false, reason: "Domain has no mail server" }

Pros: Catches fake domains. Uses built-in Node.js dns module (no dependencies).
Cons: Doesn't catch disposable emails, role accounts, or typos. DNS lookups add ~10-50ms latency.

3. Disposable Email Detection (Better)

About 60% of fake signups use disposable email services like Mailinator, Guerrilla Mail, or 10MinuteMail. You need a blocklist.

// Maintain a Set of known disposable domains
const disposableDomains = new Set([
  "mailinator.com",
  "guerrillamail.com",
  "tempmail.com",
  "throwaway.email",
  "yopmail.com",
  // ... 500+ more domains
]);

function isDisposable(email) {
  const domain = email.split("@")[1].toLowerCase();
  return disposableDomains.has(domain);
}

console.log(isDisposable("user@mailinator.com")); // true
console.log(isDisposable("user@gmail.com"));      // false

Pros: Blocks the majority of throwaway signups.
Cons: You need to maintain the list yourself. New disposable services appear constantly. Combining this with regex + MX checks requires writing orchestration code.

4. API-Based Validation (Best)

An API handles all checks in one call: syntax, MX records, disposable detection, typo suggestions, role account detection, and more.

// Using MXCheck API via fetch (no dependencies)
async function validateEmail(email) {
  const res = await fetch(
    `https://mxcheck.dev/api/validate?email=${encodeURIComponent(email)}`
  );
  return await res.json();
}

const result = await validateEmail("admin@mailinator.com");
console.log(result);
// {
//   email: "admin@mailinator.com",
//   valid: true,
//   score: 0.6,
//   checks: {
//     syntax: { valid: true },
//     mx: { valid: true, records: ["mail.mailinator.com"] },
//     disposable: true,
//     role_account: true,
//     free_provider: false
//   },
//   suggestion: null
// }

The API also catches typos:

const result = await validateEmail("user@gmial.com");
console.log(result.suggestion); // "user@gmail.com"

Try MXCheck Free

3,000 free requests per month. Syntax, MX, disposable, typo detection in one call.

Get Free API Key

Comparison

Method Catches fake format Catches fake domains Catches disposable Catches typos Latency
Regex Yes No No No <1ms
MX Lookup Yes Yes No No 10-50ms
Blocklist Yes No Yes No <1ms
API (MXCheck) Yes Yes Yes Yes ~5ms

Which approach should you use?

Quick start: Add email validation to Express.js

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

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

  // Validate email before creating account
  const check = await fetch(
    `https://mxcheck.dev/api/validate?email=${encodeURIComponent(email)}`
  );
  const result = await check.json();

  if (!result.valid) {
    return res.status(400).json({
      error: "Invalid email address",
      reason: result.reason,
      suggestion: result.suggestion
    });
  }

  if (result.checks.disposable) {
    return res.status(400).json({
      error: "Disposable email addresses are not allowed"
    });
  }

  // Email is valid, proceed with signup
  // ... create user account
  res.json({ success: true });
});

app.listen(3000);

That's it. One API call in your signup flow blocks fake emails, catches typos before they become bounces, and flags disposable addresses before they waste your resources.