Developer Guide 11 min read

Building a Fraud Prevention Middleware in Node.js

Matt King

Matt King

September 15, 2025

Building a Fraud Prevention Middleware in Node.js

Fraud prevention middleware is a layer in your Node.js application that intercepts requests and evaluates them for fraud signals before they reach your business logic. Instead of scattering validation checks across controllers, middleware centralises the logic and applies it consistently to every relevant route.

This tutorial builds a production-ready Express middleware that validates emails, checks IP addresses, and blocks high-risk requests automatically.

What You'll Build

By the end of this guide, you'll have middleware that:

  1. Extracts the user's email and IP from incoming requests
  2. Validates them against Fidro's fraud detection API
  3. Blocks requests with disposable emails or high risk scores
  4. Caches results to avoid redundant API calls
  5. Fails open gracefully when the API is unreachable

Prerequisites

  • Node.js 18+ and Express
  • A Fidro API key (free plan includes 200 validations/month)
  • Redis (optional, for caching)

Step 1: Create the Middleware File

// middleware/fraudPrevention.js
const axios = require('axios');

const FIDRO_API_KEY = process.env.FIDRO_API_KEY;
const FIDRO_BASE_URL = 'https://api.fidro.io/v1';
const RISK_THRESHOLD = 0.7;
const TIMEOUT_MS = 3000;

// Simple in-memory cache (use Redis in production)
const cache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async function validateWithFidro(email, ip) {
  const cacheKey = `${email}:${ip}`;
  const cached = cache.get(cacheKey);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.result;
  }

  const response = await axios.post(
    `${FIDRO_BASE_URL}/validate/email`,
    { email, ip },
    {
      headers: { Authorization: `Bearer ${FIDRO_API_KEY}` },
      timeout: TIMEOUT_MS,
    }
  );

  const result = response.data;
  cache.set(cacheKey, { result, timestamp: Date.now() });
  return result;
}

function fraudPrevention(options = {}) {
  const threshold = options.riskThreshold || RISK_THRESHOLD;
  const blockDisposable = options.blockDisposable !== false;

  return async (req, res, next) => {
    const email = req.body?.email;
    const ip = req.ip || req.connection?.remoteAddress;

    if (!email) return next();

    try {
      const validation = await validateWithFidro(email, ip);

      // Block disposable emails
      if (blockDisposable && validation.disposable) {
        return res.status(422).json({
          error: 'Please use a permanent email address.',
          code: 'DISPOSABLE_EMAIL',
        });
      }

      // Block high-risk requests
      if (validation.risk_score > threshold) {
        return res.status(422).json({
          error: 'Unable to process this request.',
          code: 'HIGH_RISK',
        });
      }

      // Attach validation data for downstream use
      req.fraudCheck = validation;
      next();
    } catch (error) {
      // Fail open: allow the request if API is unreachable
      console.warn('Fraud check failed, allowing request:', error.message);
      req.fraudCheck = null;
      next();
    }
  };
}

module.exports = fraudPrevention;

Step 2: Register the Middleware

Apply it to specific routes rather than globally. You only need fraud checks on signup, checkout, and similar sensitive endpoints:

const express = require('express');
const fraudPrevention = require('./middleware/fraudPrevention');

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

// Apply to signup route
app.post('/api/signup', fraudPrevention(), async (req, res) => {
  // req.fraudCheck contains the validation result
  const user = await createUser(req.body);
  res.json({ user });
});

// Apply to checkout with stricter threshold
app.post('/api/checkout', fraudPrevention({ riskThreshold: 0.5 }), async (req, res) => {
  const order = await processOrder(req.body);
  res.json({ order });
});

// No fraud check needed for public endpoints
app.get('/api/products', async (req, res) => {
  res.json(await getProducts());
});

Step 3: Add Redis Caching for Production

The in-memory cache works for single-server deployments, but production applications need shared caching:

const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);

async function getCachedValidation(key) {
  const cached = await redis.get(`fraud:${key}`);
  return cached ? JSON.parse(cached) : null;
}

async function setCachedValidation(key, result) {
  await redis.setex(`fraud:${key}`, 86400, JSON.stringify(result));
}

Step 4: Add Monitoring

Log blocked requests so you can monitor false positives and adjust thresholds:

if (validation.disposable) {
  console.log('Blocked disposable email', {
    email,
    ip,
    domain: email.split('@')[1],
    timestamp: new Date().toISOString(),
  });
}

Review these logs weekly for the first month. If legitimate users are being blocked, adjust your threshold or add domain allowlists.

How This Translates to Other Frameworks

The core logic is framework-agnostic. Here's the same pattern in Fastify:

// Fastify hook
fastify.addHook('preHandler', async (request, reply) => {
  if (!request.body?.email) return;
  const validation = await validateWithFidro(request.body.email, request.ip);
  if (validation.disposable) {
    reply.code(422).send({ error: 'Please use a permanent email address.' });
  }
  request.fraudCheck = validation;
});

Next Steps

  1. Get your free API key — 200 validations/month included
  2. Read the API documentation for the full response schema
  3. Deploy the middleware to staging and monitor for two weeks before going live
  4. Adjust your risk threshold based on observed false positive rates

Frequently Asked Questions

What is fraud prevention middleware?

Fraud prevention middleware is a layer in your application stack that intercepts incoming requests and evaluates them for fraud signals — such as disposable emails, suspicious IPs, or high risk scores — before the request reaches your business logic. It acts as an automated gatekeeper.

Should fraud checks happen in middleware or in the controller?

Middleware is preferred for fraud checks that apply across multiple routes (signup, checkout, API key creation). Controller-level checks are better for route-specific logic. Most teams use middleware for broad email and IP validation, then add controller-level checks for payment-specific fraud signals.

How do I prevent middleware from slowing down my API?

Use three strategies: set aggressive timeouts (2-3 seconds), cache validation results for repeat requests, and fail open if the fraud API is unreachable. This ensures your application stays fast even if the validation service has latency spikes.

Can I use this middleware with frameworks other than Express?

Yes. The pattern translates directly to Fastify, Koa, Hapi, and NestJS. The core logic — extract email/IP, call validation API, evaluate risk score, allow or block — is framework-agnostic. Only the middleware registration syntax changes.

What risk score threshold should I use to block requests?

Start with 0.7 as a blocking threshold and 0.4 as a flagging threshold. Monitor your false positive rate for two weeks, then adjust. Most applications settle between 0.6 and 0.8 depending on their risk tolerance.