Integrations · Updated 2026-10-20

Signup checks in Express (Node.js)

Express middleware that screens signups by email, IP and phone, with trust-proxy setup and a fail-open timeout.

Middleware

// veqen.js
export function screenSignup({ field = "email" } = {}) {
  return async (req, res, next) => {
    try {
      const response = await fetch("https://veqen-app.com/v1/signup/check", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.VEQEN_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ email: req.body[field], ip: req.ip, phone: req.body.phone }),
        signal: AbortSignal.timeout(4000),
      });
      req.veqen = response.ok ? await response.json() : null;
    } catch {
      req.veqen = null; // fail open
    }
    const verdict = req.veqen?.risk.verdict ?? "review";
    if (verdict === "refuse") {
      return res.status(400).json({ error: "Please use a permanent email address." });
    }
    req.needsVerification = verdict === "review";
    next();
  };
}

Wire it up

import express from "express";
import { screenSignup } from "./veqen.js";

const app = express();
app.set("trust proxy", 1); // behind one proxy/load balancer: req.ip is the visitor
app.use(express.json());

app.post("/signup", screenSignup(), async (req, res) => {
  const user = await createUser({
    email: req.body.email,
    canonicalEmail: req.veqen?.email?.canonical,
    verified: !req.needsVerification,
  });
  res.status(201).json({ id: user.id, verifyEmail: req.needsVerification });
});

trust proxy matters: without it, req.ip is your load balancer's address, and every signup looks like it comes from a datacenter.