← All posts · 2026-06-15
javascriptnodeemailwebdev
Search "validate email JavaScript" and you'll get a hundred regexes. Regex has its place, but it only answers "does this look like an email?", not "can this address actually receive mail?" This post covers the layers of email validation and how to add the ones regex can't.
A pragmatic pattern catches obvious garbage:
const looksValid = (email) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
Don't chase the "perfect" RFC 5322 regex: it's enormous and still won't tell you the domain exists. Use a simple pattern to reject nonsense, then move on.
What regex can't tell you:
@asdf.asdf passes regex, accepts no mail.)@mailinator.com is perfectly valid syntactically.)gmail.com instead of gmial.com?A real address needs a domain with an MX (mail exchanger) record. In Node you can check DNS yourself:
import { resolveMx } from "node:dns/promises";
async function domainAcceptsMail(domain) {
try {
const records = await resolveMx(domain);
return records.length > 0;
} catch {
return false;
}
}
This already removes a big class of fakes. But it runs only server-side, doesn't cover disposable detection or typo suggestions, and you'll end up maintaining disposable-domain lists yourself.
This is where a verification API saves you a lot of list-maintenance and DNS plumbing. Rather than rolling it all yourself, one call returns the full picture:
npm install mailguard
import { MailGuard } from "mailguard";
const mg = new MailGuard(process.env.MAILGUARD_KEY);
const result = await mg.verify("jane@gmial.com");
// {
// status: "risky",
// score: 75,
// checks: { syntax: true, mx_found: true, disposable: false, role: false },
// did_you_mean: "gmail.com"
// }
if (await mg.isDeliverable(email)) {
// safe to accept
}
The SDK is dependency-free and works in Node 18+, Bun, Deno, Cloudflare Workers, and the browser, so the same code runs on your API or your frontend.
did_you_mean is set.status === "undeliverable"; warn (don't hard-block) on "risky".
app.post("/signup", async (req, res) => {
const r = await mg.verify(req.body.email);
if (r.status === "undeliverable") return res.status(400).json({ error: "Invalid email" });
// proceed to create the account
});
actually cleans your signups, and the part not worth building from scratch.
MailGuard gives you all three layers in one call, with a free tier (500/month, no card). Grab a key and drop the SDK into your signup flow in a couple of minutes.
Try MailGuard free — 500 verifications a month, no card required. Get a key →