Skip to content
Commencez avec 100 crédits de vérification gratuits
Qualisend
Tous les articles
Ingénierie / 11 mai 2026

Comment valider une adresse e-mail dans Express

9 minutes read

Qualisend team
Une fenêtre de code Express filtrant une adresse e-mail à travers les couches de validation syntaxe, MX et boîte aux lettres

Pour valider une adresse e-mail dans Express, vous mettez en place des middlewares, pas une seule vérification — et ces middlewares doivent répondre à trois questions distinctes, dans l'ordre. L'adresse a-t-elle la bonne forme ? Son domaine peut-il recevoir du courrier ? La boîte aux lettres existe-t-elle réellement ? Express n'en fournit aucune, mais l'écosystème qui l'entoure réduit chacune à quelques lignes : express-validator pour la syntaxe, le module natif dns/promises de Node pour le domaine, et une API de vérification pour la boîte aux lettres. Ce guide construit les trois sous forme de validateurs sur une seule route POST et montre exactement où le framework s'arrête et où commence une vraie vérification. C'est la version « façon Express » du guide de validation d'e-mail en Node.js : le raisonnement sur le DNS et le SMTP qui s'y trouve reste intégralement valable.

La réponse courte#

Enchaînez trois validateurs sur le champ email, du moins coûteux au plus coûteux, et laissez le .bail() d'express-validator court-circuiter dès qu'un résultat est décisif : utilisez body('email').isEmail() pour la syntaxe, un validateur personnalisé asynchrone encapsulant resolveMx de dns/promises pour le domaine, et un second validateur personnalisé asynchrone qui appelle une API de vérification pour la boîte aux lettres — en renvoyant un 422 lorsque le verdict est undeliverable. N'essayez pas d'ouvrir vous-même des connexions SMTP depuis votre processus Express pour sonder les boîtes aux lettres : le port 25 sortant est bloqué sur la plupart des hébergeurs, et la réponse dépend de la réputation de l'IP émettrice et du greylisting que vous ne voulez pas réimplémenter. Chaque couche exclut des adresses de manière moins coûteuse que la précédente ; seule l'API peut en valider une.

Couche 1 : la syntaxe avec express-validator#

express-validator est le middleware de validation de facto pour Express — un wrapper fin et natif à Express autour de la bibliothèque validator.js. Sa vérification isEmail() est le isEmail de validator.js : vous obtenez donc le même analyseur de syntaxe éprouvé sur lequel s'appuie déjà tout projet Node, exposé sous forme de middleware chaînable. Déclarez une règle sur le champ et elle s'exécute avant votre gestionnaire :

import { body } from "express-validator";

const validateEmail = body("email")
  .trim()
  .isEmail()
  .withMessage("Enter a valid email address.")
  .bail();

.trim() nettoie les espaces en début et en fin de chaîne, .isEmail() valide la forme, .withMessage() définit le texte d'erreur, et .bail() arrête la chaîne pour ce champ dès que la syntaxe échoue — ainsi les couches plus coûteuses ci-dessous ne s'exécutent jamais sur une chaîne mal formée. Ce .bail() est l'équivalent Express du fait de s'arrêter à la vérification la moins coûteuse possible.

Sachez exactement ce que cela vous apporte. isEmail() lit la chaîne — il ne résout jamais le DNS et n'ouvre jamais de socket. definitely-fake@gmail.com passe. info@company-that-folded.com passe. typo@gmial.com passe. Ces trois adresses sont non délivrables, et aucun validateur qui se contente d'inspecter la chaîne ne vous le dira jamais — la même raison pour laquelle la validation d'e-mail par regex échoue. Syntaxe et délivrabilité sont deux questions différentes : l'une est un fait à propos de la chaîne, l'autre un fait à propos d'Internet.

Couche 2 : le domaine peut-il recevoir du courrier ?#

C'est la première couche qu'Express ne vous offre pas, et elle est peu coûteuse à ajouter. Un domaine sans enregistrement MX ne peut accepter de courrier pour personne : une seule recherche DNS élimine donc les domaines morts, les noms d'entreprise mal orthographiés et les TLD inventés avant de dépenser quoi que ce soit sur le réseau. Le module natif node:dns/promises de Node résout les enregistrements MX sans aucune dépendance, et le .custom() d'express-validator accepte une fonction asynchrone — levez à l'intérieur pour faire échouer le champ, renvoyez true pour le faire réussir :

import { resolveMx } from "node:dns/promises";

async function hasMailRoute(email) {
  const domain = email.slice(email.lastIndexOf("@") + 1);

  let records = [];
  try {
    records = await resolveMx(domain);
  } catch {
    // ENOTFOUND / ENODATA — the domain doesn't exist or publishes no MX
    throw new Error("This domain cannot receive email.");
  }

  if (records.length === 0) {
    throw new Error("This domain cannot receive email.");
  }
  return true;
}

Comme il s'agit d'une simple fonction asynchrone, elle s'insère directement dans la chaîne en tant que validateur personnalisé, protégée par un autre .bail() afin que la couche « boîte aux lettres » ne s'exécute que sur un domaine qui résout effectivement une route de courrier :

body("email")
  .isEmail()
  .bail()
  .custom(hasMailRoute)
  .bail();
await hasMailRoute("jane@gmail.com");               // true
await hasMailRoute("jane@company-that-folded.com"); // throws — no MX

Certains domaines acceptent le courrier sur un enregistrement A sans MX (MX implicite). Si vous voulez prendre en compte ce cas limite, repliez-vous sur resolve4 lorsque resolveMx revient vide — mais pour l'écrasante majorité des adresses réelles, une vérification MX est le bon filtre.

Couche 3 : la boîte aux lettres existe-t-elle réellement ?#

Les couches 1 et 2 ne peuvent qu'exclure une adresse. Un domaine peut publier des enregistrements MX parfaits et ne toujours avoir aucune boîte aux lettres à l'adresse que vous détenez — noreply-9f2x@gmail.com a une syntaxe valide sur une route de courrier active, et c'est pourtant une boîte aux lettres qui n'a jamais été créée. Confirmer une boîte aux lettres précise implique la conversation de livraison SMTP : se connecter à l'hôte de messagerie, émettre RCPT TO, lire la réponse et se déconnecter avant d'envoyer quoi que ce soit.

En principe, vous pourriez scripter cela depuis Express avec le module net. En pratique, vous ne devriez pas l'exécuter depuis votre serveur applicatif : la plupart des hébergeurs cloud bloquent le port 25 sortant, la réponse dépend de la réputation de l'IP depuis laquelle vous vous connectez, et les serveurs destinataires appliquent le greylisting et limitent le débit des expéditeurs inconnus — de sorte qu'un sondage qui fonctionne dans un test local échoue discrètement, ou vous fait mettre sur liste de blocage, en production. Comment fonctionne la vérification d'e-mail parcourt tout le pipeline, domaines catch-all inclus. C'est la couche qui vaut la peine d'être déléguée.

Appeler Qualisend depuis un validateur personnalisé asynchrone#

La délégation n'est qu'un validateur personnalisé de plus. Le point de terminaison de vérification de Qualisend exécute tout le pipeline — syntaxe, DNS et le sondage SMTP de la boîte aux lettres — depuis une infrastructure à réputation gérée, et renvoie un verdict. Appelez-le avec le fetch global (intégré à Node 18+), lisez votre clé depuis une variable d'environnement, et ne levez que lorsque le verdict est undeliverable :

async function isDeliverable(email) {
  const res = await fetch("https://api.qualisend.com/v1/verify", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.QUALISEND_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
  });

  // On our own outage or a rate limit, don't block a real signup.
  if (!res.ok) return true;

  const { result } = await res.json();

  if (result.status === "undeliverable") {
    throw new Error("We couldn't confirm a mailbox at this address.");
  }
  return true;
}

Le point de terminaison ci-dessus est un espace réservé — consultez la référence de l'API pour l'URL de base et la forme exacte de la requête — mais la réponse revient sous la forme d'une enveloppe { "result": { ... } } :

{
  "result": {
    "status": "deliverable",
    "score": 95,
    "reason": null,
    "sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false }
  }
}

status vaut l'une des valeurs deliverable, risky, undeliverable ou unknown. Le validateur ci-dessus ne fait échouer durement que undeliverable et laisse passer risky et unknown, afin que vous puissiez décider en aval quoi en faire — filtrer sur le score, ou faire une distinction selon une entrée de sub_flags comme disposable — au lieu de transformer une adresse limite en erreur de formulaire. Traitez le JSON ci-dessus comme la forme, pas comme le contrat ; la liste complète des champs se trouve dans la documentation développeur.

Assembler les couches pour valider une adresse e-mail dans Express#

Composez les trois sur une seule chaîne body("email"), du moins coûteux au plus coûteux, avec un .bail() entre chacune afin qu'un échec court-circuite avant que la couche suivante ne s'exécute. Le validateur de l'API ne se déclenche que sur une adresse qui a déjà franchi la syntaxe et la vérification MX :

import { body } from "express-validator";

const validateEmail = [
  body("email")
    .trim()
    .isEmail()
    .withMessage("Enter a valid email address.")
    .bail()
    .custom(hasMailRoute)
    .bail()
    .custom(isDeliverable),
];

Branchez ce tableau devant votre gestionnaire de route, puis lisez les erreurs collectées avec validationResult. Lorsque la chaîne échoue, répondez avec un 422 et le tableau d'erreurs ; sinon, chaque couche a réussi et l'adresse peut être enregistrée sans risque :

import express from "express";
import { validationResult } from "express-validator";

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

app.post("/signup", validateEmail, (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() });
  }

  // Every layer passed — safe to create the account.
  const { email } = req.body;
  return res.status(201).json({ email });
});

Cet ordre est toute l'astuce : isEmail() court-circuite les déchets avant que votre code ne s'exécute, la vérification MX locale écarte gratuitement les domaines morts, et l'API n'est sollicitée que pour les adresses qui ont franchi les deux. Une validation dans le mauvais ordre — ou le fait de sauter les couches peu coûteuses — dépense un crédit d'API à chaque faute de frappe. C'est la mise en couches, et non le framework, qui rend la validation fiable ; elle a la même forme que la version Node.js, la chaîne de middlewares d'Express tenant lieu de pipeline.

Une décision à prendre d'emblée : que se passe-t-il lorsque l'appel à l'API lui-même échoue. Un délai réseau dépassé ou un fetch qui lève à l'intérieur d'isDeliverable ne devrait pas infliger à un vrai client un 422 qu'il ne peut pas corriger. Le validateur ci-dessus adoucit déjà ce cas en renvoyant true sur une réponse non OK — traitant un service injoignable comme unknown plutôt que comme undeliverable, laissant passer l'inscription et reportant la revérification à plus tard. Les couches syntaxe et MX se sont déjà exécutées localement : vous ne relâchez donc que la couche qui dépend du réseau.

Exécutez ce pipeline synchrone rapide au point de collecte et réservez la vérification par lots, plus lourde, au nettoyage de listes. Le guide d'inscription serverless montre le schéma en temps réel de bout en bout, et lorsque vous choisissez le service qui alimente la troisième couche, la comparaison des API aligne les options.

Foire aux questions#

Le isEmail d'express-validator suffit-il à valider une adresse e-mail ?#

Pour la syntaxe, oui — body("email").isEmail() encapsule le isEmail de validator.js et constitue la bonne vérification de premier niveau, bien meilleure qu'une regex codée à la main. Mais il valide la forme, pas la délivrabilité : il ne résout jamais le DNS et ne contacte jamais de serveur de messagerie, donc une réussite signifie « ça ressemble à un e-mail », pas « ça sera bien délivré ». Associez-le à une recherche MX et à une vérification de la boîte aux lettres avant de faire confiance à l'adresse.

Comment écrire un validateur personnalisé asynchrone dans Express ?#

Passez une fonction asynchrone au .custom() d'express-validator. La fonction reçoit la valeur du champ ; renvoyez true (ou résolvez) pour réussir, et throw une Error (ou rejetez) pour échouer — le message levé devient l'erreur de validation. C'est ainsi que les fonctions hasMailRoute et isDeliverable ci-dessus se branchent sur la même chaîne body("email"). Placez un .bail() avant chacune pour qu'une couche échouée arrête la chaîne au lieu d'exécuter la suivante.

Puis-je vérifier l'existence d'une boîte aux lettres dans Express sans API ?#

En partie. node:dns/promises confirme que le domaine accepte le courrier, ce qui écarte gratuitement les domaines morts et ne nécessite aucune dépendance. Confirmer la boîte aux lettres implique une conversation SMTP que vous pouvez tenter avec le module net, mais que vous ne devriez pas exécuter depuis votre serveur applicatif — le port 25 est largement bloqué et le résultat dépend de la réputation de votre IP. C'est précisément la couche qu'un service de vérification est fait pour gérer ; ce qu'est la vérification d'e-mail en couvre les termes.

Faut-il renvoyer un 422 ou laisser passer l'inscription en cas de résultat inconnu ?#

Rejetez undeliverable avec un 422 — c'est une mauvaise adresse confirmée que l'utilisateur peut corriger. Mais ne provoquez pas d'échec à cause d'une panne de votre propre service de vérification : traitez une réponse non OK, un délai dépassé ou un statut unknown comme « laisser passer et revérifier plus tard », afin qu'un problème réseau passager ne bloque jamais une inscription légitime. Le validateur isDeliverable fait exactement cela en renvoyant true lorsque la réponse n'est pas OK.


Prêt à ajouter la couche « boîte aux lettres » ? Déposez une adresse syntaxiquement parfaite dans le vérificateur d'e-mail gratuit pour voir une chaîne approuvée par isEmail revenir undeliverable, puis branchez le même verdict dans votre middleware Express grâce aux exemples à copier-coller de la référence de l'API.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started