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.
Copy
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;
}
- 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
Copy
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.
Copy
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
Copy
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
Copy
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
Copy
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.
- Store keys in environment variables
- Use secret management systems (AWS Secrets Manager, HashiCorp Vault)
- Rotate keys regularly
- Use separate keys for each environment
- 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
Copy
// ❌ 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
Copy
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
Copy
// 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
Copy
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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
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
Copy
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