Skip to main content

Overview

Follow these best practices to maximize the value of Fraudiant while optimizing performance, cost, and user experience.

Performance Optimization

Implement Caching

Caching validation results can reduce API calls by 70-90% and dramatically improve response times.
Recommended caching strategy:
const cache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async function validateEmailCached(email) {
  const cacheKey = email.toLowerCase();
  const cached = cache.get(cacheKey);

  // Check if cache exists and is not expired
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  // Fetch from API
  const validation = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  // Store in cache
  cache.set(cacheKey, {
    data: validation,
    timestamp: Date.now()
  });

  return validation;
}
Cache duration recommendations:
  • Email validation: 24-48 hours
  • Domain validation: 48-72 hours (domains change less frequently)
  • Blocklist checks: No caching (real-time updates needed)

Use Redis for Distributed Caching

const redis = require('redis');
const client = redis.createClient();

async function validateWithRedis(email) {
  const cacheKey = `email_validation:${email.toLowerCase()}`;

  // Try cache first
  const cached = await client.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }

  // Call API
  const validation = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  // Cache for 24 hours
  await client.setEx(cacheKey, 86400, JSON.stringify(validation));

  return validation;
}

Error Handling

Fail Open Strategy

If the Fraudiant API is unavailable, allow users to proceed rather than blocking legitimate signups.
async function validateEmailSafe(email) {
  try {
    const response = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` },
        timeout: 5000 // 5 second timeout
      }
    );

    if (!response.ok) {
      // Service error - fail open
      console.warn('Fraudiant service unavailable, allowing signup');
      return { valid: true, failedOpen: true };
    }

    const validation = await response.json();

    return {
      valid: !validation.disposable && !validation.spam && validation.mx,
      data: validation
    };

  } catch (error) {
    console.error('Email validation error:', error);
    // Fail open - don't block users due to service issues
    return { valid: true, failedOpen: true };
  }
}

Implement Retry Logic with Exponential Backoff

async function validateWithRetry(email, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(
        `https://api.fraudiant.com/email/${email}`,
        {
          headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
        }
      );

      if (response.status === 429) {
        // Rate limited - wait and retry
        const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }

      return await response.json();

    } catch (error) {
      if (attempt === maxRetries - 1) {
        // Last attempt failed - fail open
        return { disposable: false, spam: false, mx: true };
      }

      // Wait before retrying
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
    }
  }
}

Rate Limit Management

Monitor Rate Limit Headers

async function validateWithRateLimitCheck(email) {
  const response = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  );

  // Check rate limit headers
  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
  const limit = parseInt(response.headers.get('X-RateLimit-Limit') || '0');
  const reset = parseInt(response.headers.get('X-RateLimit-Reset') || '0');

  // Warn if approaching limit
  if (remaining < limit * 0.1) {
    console.warn(`Approaching rate limit: ${remaining}/${limit} remaining`);
    // Consider implementing queue or backoff strategy
  }

  return response.json();
}

Implement Request Queuing

class RateLimitedValidator {
  constructor(requestsPerSecond = 10) {
    this.queue = [];
    this.processing = false;
    this.interval = 1000 / requestsPerSecond;
  }

  async validate(email) {
    return new Promise((resolve, reject) => {
      this.queue.push({ email, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      const { email, resolve, reject } = this.queue.shift();

      try {
        const validation = await fetch(
          `https://api.fraudiant.com/email/${email}`,
          {
            headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
          }
        ).then(r => r.json());

        resolve(validation);
      } catch (error) {
        reject(error);
      }

      // Wait before processing next request
      await new Promise(r => setTimeout(r, this.interval));
    }

    this.processing = false;
  }
}

// Usage
const validator = new RateLimitedValidator(10); // 10 requests per second

Security Best Practices

Protect API Keys

Never expose API keys in client-side code, public repositories, or logs.
DO:
  • Store keys in environment variables
  • Use secret management systems (AWS Secrets Manager, HashiCorp Vault)
  • Rotate keys regularly
  • Use separate keys for each environment
DON’T:
  • Hardcode keys in source code
  • Commit keys to version control
  • Share keys via email or chat
  • Use production keys in development

Validate Server-Side Only

// ❌ BAD: Client-side validation only
async function clientSideValidation(email) {
  // API key exposed in client code!
  const response = await fetch(`https://api.fraudiant.com/email/${email}`, {
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' } // Never do this!
  });
  return response.json();
}

// ✅ GOOD: Server-side validation
// Frontend
async function validateEmail(email) {
  const response = await fetch('/api/validate', {
    method: 'POST',
    body: JSON.stringify({ email })
  });
  return response.json();
}

// Backend
app.post('/api/validate', async (req, res) => {
  const { email } = req.body;
  const validation = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  res.json({ valid: !validation.disposable && !validation.spam });
});

User Experience

Provide Clear Feedback

function getErrorMessage(validation) {
  if (validation.disposable) {
    return 'Temporary email addresses are not allowed. Please use a permanent email.';
  }

  if (validation.spam) {
    return 'This email domain is associated with spam. Please use a different email.';
  }

  if (!validation.mx) {
    return 'This email address cannot receive emails. Please check for typos.';
  }

  if (validation.did_you_mean) {
    return `Did you mean ${validation.did_you_mean}?`;
  }

  return 'Please provide a valid email address.';
}

Implement Progressive Validation

// Validate as user types (with debouncing)
let validationTimeout;

function onEmailInput(email) {
  clearTimeout(validationTimeout);

  validationTimeout = setTimeout(async () => {
    const validation = await validateEmail(email);

    if (validation.did_you_mean) {
      showSuggestion(validation.did_you_mean);
    }

    if (!validation.valid) {
      showError(getErrorMessage(validation.data));
    } else {
      clearError();
    }
  }, 500); // Wait 500ms after user stops typing
}

Handle Typos Gracefully

async function validateWithTypoCorrection(email) {
  const validation = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  if (validation.did_you_mean) {
    return {
      valid: true,
      suggestion: validation.did_you_mean,
      message: `Did you mean ${validation.did_you_mean}? Click to use this instead.`,
      autoCorrect: true
    };
  }

  return {
    valid: !validation.disposable && !validation.spam && validation.mx,
    data: validation
  };
}

Cost Optimization

Batch Validation

// Instead of validating each email immediately
async function processBulkEmails(emails) {
  // Deduplicate emails
  const uniqueEmails = [...new Set(emails.map(e => e.toLowerCase()))];

  // Check cache first
  const uncachedEmails = uniqueEmails.filter(email => !cache.has(email));

  // Validate only uncached emails with rate limiting
  const results = [];
  for (const email of uncachedEmails) {
    const validation = await validateEmail(email);
    cache.set(email, validation);
    results.push({ email, validation });

    // Rate limit: wait between requests
    await new Promise(r => setTimeout(r, 100)); // 10 requests per second
  }

  return results;
}

Use Domain Validation for Pre-filtering

// Validate domain first (cheaper), then email if needed
async function efficientValidation(email) {
  const domain = email.split('@')[1];

  // Check domain first
  const domainValidation = await fetch(
    `https://api.fraudiant.com/domain/${domain}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  // If domain is clearly bad, no need to validate full email
  if (domainValidation.disposable || domainValidation.spam || !domainValidation.mx) {
    return { valid: false, reason: 'Invalid domain' };
  }

  // Only validate full email if domain passed
  const emailValidation = await fetch(
    `https://api.fraudiant.com/email/${email}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
    }
  ).then(r => r.json());

  return { valid: true, data: emailValidation };
}

Testing

Create Test Helpers

// Mock validator for testing
class MockEmailValidator {
  validate(email) {
    // Return fake validation for testing
    if (email.includes('disposable')) {
      return Promise.resolve({
        disposable: true,
        spam: false,
        mx: true
      });
    }

    return Promise.resolve({
      disposable: false,
      spam: false,
      mx: true
    });
  }
}

// Use in tests
describe('User Registration', () => {
  it('should reject disposable emails', async () => {
    const validator = new MockEmailValidator();
    const result = await validator.validate('[email protected]');
    expect(result.disposable).toBe(true);
  });
});

Test with Different Environments

// config/fraudiant.js
module.exports = {
  development: {
    apiKey: process.env.FRAUDIANT_DEV_API_KEY,
    strictMode: false, // Allow more emails in dev
  },
  staging: {
    apiKey: process.env.FRAUDIANT_STAGING_API_KEY,
    strictMode: true,
  },
  production: {
    apiKey: process.env.FRAUDIANT_PROD_API_KEY,
    strictMode: true,
  }
};

Monitoring & Observability

Log Validation Results

async function validateWithLogging(email) {
  const startTime = Date.now();

  try {
    const validation = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: { 'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}` }
      }
    ).then(r => r.json());

    const duration = Date.now() - startTime;

    // Log metrics
    console.log({
      action: 'email_validation',
      email: email.split('@')[1], // Log domain only for privacy
      disposable: validation.disposable,
      spam: validation.spam,
      duration,
      timestamp: new Date().toISOString()
    });

    return validation;

  } catch (error) {
    console.error({
      action: 'email_validation_error',
      error: error.message,
      duration: Date.now() - startTime
    });

    throw error;
  }
}

Track Rejection Rates

const metrics = {
  total: 0,
  rejected: 0,
  disposable: 0,
  spam: 0
};

async function validateWithMetrics(email) {
  metrics.total++;

  const validation = await validateEmail(email);

  if (validation.disposable) {
    metrics.rejected++;
    metrics.disposable++;
  }

  if (validation.spam) {
    metrics.rejected++;
    metrics.spam++;
  }

  // Log metrics periodically
  if (metrics.total % 100 === 0) {
    console.log({
      rejectionRate: (metrics.rejected / metrics.total * 100).toFixed(2) + '%',
      disposableRate: (metrics.disposable / metrics.total * 100).toFixed(2) + '%',
      spamRate: (metrics.spam / metrics.total * 100).toFixed(2) + '%'
    });
  }

  return validation;
}

Summary

Cache aggressively

Reduce API calls by 70-90% with proper caching

Fail open gracefully

Don’t block users when service is unavailable

Monitor rate limits

Track usage and implement queuing strategies

Validate server-side

Never expose API keys in client code

Provide clear feedback

Help users understand validation failures

Test thoroughly

Use separate environments and mock validators