Node.js Integration GuideΒΆ

This guide covers everything you need to integrate ReputeAPI into your Node.js applications, from basic setup to advanced patterns.

πŸš€ Quick StartΒΆ

1. InstallationΒΆ

# Install the official ReputeAPI Node.js SDK
npm install @reputeapi/node

# Or with Yarn
yarn add @reputeapi/node

2. Basic SetupΒΆ

const ReputeAPI = require('@reputeapi/node');

// Initialize client with API key
const client = new ReputeAPI(process.env.REPUTEAPI_KEY);

// Validate a domain
async function validateDomain(domain) {
  try {
    const result = await client.check(domain);
    console.log(`Security score for ${domain}: ${result.score}/100`);
    return result;
  } catch (error) {
    console.error('Validation failed:', error.message);
    throw error;
  }
}

// Example usage
validateDomain('example.com')
  .then(result => console.log('Validation complete'))
  .catch(err => console.error('Error:', err));

3. First API CallΒΆ

const express = require('express');
const ReputeAPI = require('@reputeapi/node');

const app = express();
const reputeApi = new ReputeAPI(process.env.REPUTEAPI_KEY);

app.use(express.json());

app.post('/validate-domain', async (req, res) => {
  try {
    const { domain } = req.body;

    if (!domain) {
      return res.status(400).json({ error: 'Domain is required' });
    }

    const validation = await reputeApi.check(domain);

    res.json({
      domain: domain,
      score: validation.score,
      status: validation.score >= 70 ? 'secure' : 'needs_improvement',
      recommendations: validation.recommendations
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

βš™οΈ Configuration OptionsΒΆ

Environment-Based ConfigurationΒΆ

// config/reputeapi.js
const config = {
  development: {
    apiKey: process.env.REPUTEAPI_TEST_KEY,
    endpoint: 'https://api.reputeapi.com',
    timeout: 30000,
    retries: 3,
    logLevel: 'debug'
  },
  production: {
    apiKey: process.env.REPUTEAPI_LIVE_KEY,
    endpoint: 'https://api.reputeapi.com',
    timeout: 10000,
    retries: 5,
    logLevel: 'warn'
  }
};

const env = process.env.NODE_ENV || 'development';
module.exports = config[env];

Advanced Client ConfigurationΒΆ

const ReputeAPI = require('@reputeapi/node');

const client = new ReputeAPI({
  apiKey: process.env.REPUTEAPI_KEY,

  // Custom endpoint (for enterprise customers)
  endpoint: 'https://api.reputeapi.com',

  // Request timeout in milliseconds
  timeout: 30000,

  // Retry configuration
  retries: {
    attempts: 3,
    delay: 1000,
    backoff: 'exponential'
  },

  // Rate limiting configuration
  rateLimit: {
    enabled: true,
    maxRequests: 100,
    perInterval: 'hour'
  },

  // Logging configuration
  logging: {
    level: 'info',
    requests: true,
    responses: false
  }
});

πŸ”§ Core FeaturesΒΆ

Single Domain ValidationΒΆ

async function validateSingleDomain() {
  const domain = 'example.com';

  try {
    const result = await client.check(domain);

    console.log('Domain:', result.domain);
    console.log('Score:', result.score);
    console.log('SPF Valid:', result.spf.valid);
    console.log('DKIM Valid:', result.dkim.valid);
    console.log('DMARC Valid:', result.dmarc.valid);

    // Access detailed findings
    result.findings.forEach(finding => {
      console.log(`${finding.severity}: ${finding.message}`);
    });

    return result;
  } catch (error) {
    if (error.code === 'RATE_LIMITED') {
      console.log('Rate limited. Retry after:', error.retryAfter);
    } else {
      console.error('Validation error:', error.message);
    }
    throw error;
  }
}

Bulk Domain ValidationΒΆ

async function validateMultipleDomains() {
  const domains = [
    'example.com',
    'test.com', 
    'mydomain.org'
  ];

  try {
    const results = await client.bulk(domains);

    // Process results
    results.forEach(result => {
      console.log(`${result.domain}: ${result.score}/100`);

      if (result.score < 70) {
        console.log('Recommendations:');
        result.recommendations.forEach(rec => {
          console.log(`- ${rec.title}: ${rec.description}`);
        });
      }
    });

    // Calculate summary statistics
    const averageScore = results.reduce((sum, r) => sum + r.score, 0) / results.length;
    const failedDomains = results.filter(r => r.score < 70).length;

    console.log(`Average score: ${averageScore.toFixed(1)}`);
    console.log(`Domains needing attention: ${failedDomains}`);

    return results;
  } catch (error) {
    console.error('Bulk validation failed:', error);
    throw error;
  }
}

Historical DataΒΆ

async function getHistoricalData() {
  const domain = 'example.com';

  try {
    const history = await client.getHistory(domain, {
      days: 30,           // Last 30 days
      includeDetails: true // Include full validation details
    });

    console.log(`Retrieved ${history.length} historical records`);

    // Analyze trends
    const scores = history.map(h => h.score);
    const trend = calculateTrend(scores);

    console.log('Score trend:', trend);

    return history;
  } catch (error) {
    console.error('Failed to retrieve history:', error);
    throw error;
  }
}

function calculateTrend(scores) {
  if (scores.length < 2) return 'insufficient_data';

  const recent = scores.slice(-5).reduce((a, b) => a + b) / 5;
  const older = scores.slice(0, 5).reduce((a, b) => a + b) / 5;

  const change = recent - older;

  if (change > 5) return 'improving';
  if (change < -5) return 'declining';
  return 'stable';
}

🎯 Real-World Examples¢

Express.js API ServerΒΆ

const express = require('express');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const ReputeAPI = require('@reputeapi/node');

const app = express();
const reputeApi = new ReputeAPI(process.env.REPUTEAPI_KEY);

// Middleware
app.use(helmet());
app.use(express.json({ limit: '10mb' }));

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api', limiter);

// Validation endpoint
app.post('/api/validate', async (req, res) => {
  const { domain, includeRecommendations = true } = req.body;

  // Input validation
  if (!domain || typeof domain !== 'string') {
    return res.status(400).json({
      error: 'Invalid domain parameter'
    });
  }

  try {
    const startTime = Date.now();
    const result = await reputeApi.check(domain);
    const duration = Date.now() - startTime;

    // Log for monitoring
    console.log(`Validated ${domain} in ${duration}ms (score: ${result.score})`);

    // Format response
    const response = {
      domain: result.domain,
      score: result.score,
      timestamp: new Date().toISOString(),
      processing_time: duration,
      security: {
        spf: result.spf,
        dkim: result.dkim,
        dmarc: result.dmarc
      }
    };

    if (includeRecommendations) {
      response.recommendations = result.recommendations;
    }

    res.json(response);
  } catch (error) {
    console.error('Validation error:', error);

    if (error.code === 'RATE_LIMITED') {
      return res.status(429).json({
        error: 'Rate limit exceeded',
        retryAfter: error.retryAfter
      });
    }

    res.status(500).json({
      error: 'Validation failed',
      message: error.message
    });
  }
});

// Bulk validation endpoint
app.post('/api/validate/bulk', async (req, res) => {
  const { domains } = req.body;

  if (!Array.isArray(domains) || domains.length === 0) {
    return res.status(400).json({
      error: 'Invalid domains array'
    });
  }

  if (domains.length > 100) {
    return res.status(400).json({
      error: 'Maximum 100 domains per request'
    });
  }

  try {
    const results = await reputeApi.bulk(domains);

    // Generate summary
    const summary = {
      total_domains: results.length,
      average_score: results.reduce((sum, r) => sum + r.score, 0) / results.length,
      high_risk: results.filter(r => r.score < 50).length,
      medium_risk: results.filter(r => r.score >= 50 && r.score < 70).length,
      low_risk: results.filter(r => r.score >= 70).length
    };

    res.json({
      summary,
      results
    });
  } catch (error) {
    console.error('Bulk validation error:', error);
    res.status(500).json({
      error: 'Bulk validation failed',
      message: error.message
    });
  }
});

app.listen(process.env.PORT || 3000, () => {
  console.log(`Server running on port ${process.env.PORT || 3000}`);
});

Background Job ProcessingΒΆ

const Queue = require('bull');
const ReputeAPI = require('@reputeapi/node');

// Initialize queue and API client
const validationQueue = new Queue('domain validation');
const reputeApi = new ReputeAPI(process.env.REPUTEAPI_KEY);

// Job processor
validationQueue.process('validate-domain', async (job) => {
  const { domain, userId, webhookUrl } = job.data;

  try {
    console.log(`Processing validation for ${domain}`);

    const result = await reputeApi.check(domain);

    // Store result in database
    await saveValidationResult({
      userId,
      domain,
      score: result.score,
      details: result,
      timestamp: new Date()
    });

    // Send webhook notification
    if (webhookUrl) {
      await sendWebhook(webhookUrl, {
        event: 'validation_complete',
        domain,
        score: result.score,
        userId
      });
    }

    console.log(`Validation complete for ${domain}: ${result.score}/100`);

    return result;
  } catch (error) {
    console.error(`Validation failed for ${domain}:`, error);

    // Send error notification
    if (webhookUrl) {
      await sendWebhook(webhookUrl, {
        event: 'validation_error',
        domain,
        error: error.message,
        userId
      });
    }

    throw error;
  }
});

// Add job to queue
async function queueDomainValidation(domain, userId, webhookUrl) {
  const job = await validationQueue.add('validate-domain', {
    domain,
    userId,
    webhookUrl
  }, {
    attempts: 3,
    backoff: 'exponential',
    delay: 5000
  });

  console.log(`Queued validation job ${job.id} for domain ${domain}`);
  return job;
}

// Helper functions
async function saveValidationResult(data) {
  // Implement database save logic
  console.log('Saving validation result:', data.domain);
}

async function sendWebhook(url, payload) {
  // Implement webhook delivery
  console.log('Sending webhook:', payload);
}

Database IntegrationΒΆ

const mongoose = require('mongoose');
const ReputeAPI = require('@reputeapi/node');

// Domain validation schema
const ValidationSchema = new mongoose.Schema({
  domain: { type: String, required: true, index: true },
  score: { type: Number, required: true },
  spf: { type: Object },
  dkim: { type: Object },
  dmarc: { type: Object },
  recommendations: [{ type: Object }],
  findings: [{ type: Object }],
  validatedAt: { type: Date, default: Date.now },
  validatedBy: { type: String }, // API key or user ID
});

const Validation = mongoose.model('Validation', ValidationSchema);

class DomainValidator {
  constructor(apiKey) {
    this.client = new ReputeAPI(apiKey);
  }

  async validateAndStore(domain, userId = null) {
    try {
      // Check if recent validation exists
      const recent = await Validation.findOne({
        domain,
        validatedAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
      });

      if (recent) {
        console.log(`Using cached validation for ${domain}`);
        return recent;
      }

      // Perform new validation
      const result = await this.client.check(domain);

      // Store in database
      const validation = new Validation({
        domain: result.domain,
        score: result.score,
        spf: result.spf,
        dkim: result.dkim,
        dmarc: result.dmarc,
        recommendations: result.recommendations,
        findings: result.findings,
        validatedBy: userId
      });

      await validation.save();

      console.log(`Stored validation for ${domain} (score: ${result.score})`);
      return validation;
    } catch (error) {
      console.error(`Failed to validate and store ${domain}:`, error);
      throw error;
    }
  }

  async getDomainHistory(domain, days = 30) {
    const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);

    const history = await Validation.find({
      domain,
      validatedAt: { $gte: since }
    }).sort({ validatedAt: -1 });

    return history;
  }

  async getScoreTrend(domain, days = 30) {
    const history = await this.getDomainHistory(domain, days);

    if (history.length < 2) {
      return { trend: 'insufficient_data' };
    }

    const scores = history.map(h => h.score);
    const firstScore = scores[scores.length - 1];
    const lastScore = scores[0];
    const change = lastScore - firstScore;

    return {
      trend: change > 5 ? 'improving' : change < -5 ? 'declining' : 'stable',
      change,
      currentScore: lastScore,
      previousScore: firstScore,
      dataPoints: scores.length
    };
  }
}

module.exports = DomainValidator;

πŸ”„ Error Handling & RetriesΒΆ

Comprehensive Error HandlingΒΆ

const ReputeAPI = require('@reputeapi/node');

class RobustValidator {
  constructor(apiKey) {
    this.client = new ReputeAPI(apiKey);
    this.maxRetries = 3;
    this.baseDelay = 1000; // 1 second
  }

  async validateWithRetry(domain, retries = 0) {
    try {
      return await this.client.check(domain);
    } catch (error) {
      console.error(`Validation attempt ${retries + 1} failed:`, error.message);

      // Handle specific error types
      switch (error.code) {
        case 'RATE_LIMITED':
          const delay = error.retryAfter * 1000 || this.calculateDelay(retries);
          console.log(`Rate limited. Waiting ${delay}ms before retry`);
          await this.sleep(delay);
          break;

        case 'NETWORK_ERROR':
          if (retries < this.maxRetries) {
            const delay = this.calculateDelay(retries);
            console.log(`Network error. Retrying in ${delay}ms`);
            await this.sleep(delay);
            break;
          }
          throw error;

        case 'INVALID_DOMAIN':
          // Don't retry for invalid domains
          throw error;

        case 'QUOTA_EXCEEDED':
          // Don't retry for quota issues
          throw error;

        default:
          if (retries < this.maxRetries) {
            const delay = this.calculateDelay(retries);
            await this.sleep(delay);
            break;
          }
          throw error;
      }

      if (retries < this.maxRetries) {
        return this.validateWithRetry(domain, retries + 1);
      } else {
        throw new Error(`Failed to validate ${domain} after ${this.maxRetries + 1} attempts`);
      }
    }
  }

  calculateDelay(attempt) {
    // Exponential backoff with jitter
    const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
    const jitter = Math.random() * 1000; // Add up to 1 second of jitter
    return Math.min(exponentialDelay + jitter, 30000); // Cap at 30 seconds
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const validator = new RobustValidator(process.env.REPUTEAPI_KEY);

validator.validateWithRetry('example.com')
  .then(result => console.log('Validation successful:', result.score))
  .catch(error => console.error('Final validation failure:', error.message));

πŸ“Š Monitoring & LoggingΒΆ

Request/Response LoggingΒΆ

const winston = require('winston');
const ReputeAPI = require('@reputeapi/node');

// Configure logger
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'reputeapi.log' }),
    new winston.transports.Console()
  ]
});

// Create client with logging
const client = new ReputeAPI({
  apiKey: process.env.REPUTEAPI_KEY,

  // Request interceptor
  beforeRequest: (config) => {
    logger.info('API Request', {
      method: config.method,
      url: config.url,
      domain: config.data?.domain,
      timestamp: new Date().toISOString()
    });
    return config;
  },

  // Response interceptor
  afterResponse: (response, duration) => {
    logger.info('API Response', {
      status: response.status,
      duration: `${duration}ms`,
      domain: response.data?.domain,
      score: response.data?.score,
      timestamp: new Date().toISOString()
    });
    return response;
  },

  // Error interceptor
  onError: (error) => {
    logger.error('API Error', {
      message: error.message,
      code: error.code,
      status: error.status,
      timestamp: new Date().toISOString()
    });
    throw error;
  }
});

Performance MonitoringΒΆ

const client = require('prom-client');

// Create metrics
const requestDuration = new client.Histogram({
  name: 'reputeapi_request_duration_seconds',
  help: 'Duration of ReputeAPI requests',
  labelNames: ['method', 'status']
});

const requestTotal = new client.Counter({
  name: 'reputeapi_requests_total',
  help: 'Total number of ReputeAPI requests',
  labelNames: ['method', 'status']
});

const scoreGauge = new client.Gauge({
  name: 'reputeapi_domain_score',
  help: 'Latest domain security score',
  labelNames: ['domain']
});

// Instrumented validation function
async function instrumentedValidation(domain) {
  const timer = requestDuration.startTimer({ method: 'check' });

  try {
    const result = await reputeApi.check(domain);

    // Record metrics
    timer({ status: 'success' });
    requestTotal.inc({ method: 'check', status: 'success' });
    scoreGauge.set({ domain }, result.score);

    return result;
  } catch (error) {
    timer({ status: 'error' });
    requestTotal.inc({ method: 'check', status: 'error' });
    throw error;
  }
}

// Metrics endpoint
app.get('/metrics', (req, res) => {
  res.set('Content-Type', client.register.contentType);
  res.end(client.register.metrics());
});

πŸ§ͺ TestingΒΆ

Unit Tests with JestΒΆ

// __tests__/domain-validator.test.js
const ReputeAPI = require('@reputeapi/node');
const DomainValidator = require('../src/domain-validator');

// Mock the ReputeAPI client
jest.mock('@reputeapi/node');

describe('DomainValidator', () => {
  let validator;
  let mockClient;

  beforeEach(() => {
    mockClient = {
      check: jest.fn(),
      bulk: jest.fn()
    };

    ReputeAPI.mockImplementation(() => mockClient);
    validator = new DomainValidator('test-api-key');
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  test('should validate a single domain successfully', async () => {
    const mockResult = {
      domain: 'example.com',
      score: 85,
      spf: { valid: true },
      dkim: { valid: true },
      dmarc: { valid: true }
    };

    mockClient.check.mockResolvedValue(mockResult);

    const result = await validator.validate('example.com');

    expect(mockClient.check).toHaveBeenCalledWith('example.com');
    expect(result).toEqual(mockResult);
  });

  test('should handle validation errors gracefully', async () => {
    const error = new Error('Invalid domain');
    error.code = 'INVALID_DOMAIN';

    mockClient.check.mockRejectedValue(error);

    await expect(validator.validate('invalid-domain'))
      .rejects.toThrow('Invalid domain');
  });

  test('should validate multiple domains', async () => {
    const mockResults = [
      { domain: 'example.com', score: 85 },
      { domain: 'test.com', score: 92 }
    ];

    mockClient.bulk.mockResolvedValue(mockResults);

    const domains = ['example.com', 'test.com'];
    const results = await validator.validateBulk(domains);

    expect(mockClient.bulk).toHaveBeenCalledWith(domains);
    expect(results).toEqual(mockResults);
  });
});

Integration TestsΒΆ

// __tests__/integration.test.js
const ReputeAPI = require('@reputeapi/node');

describe('ReputeAPI Integration', () => {
  let client;

  beforeAll(() => {
    // Use test API key for integration tests
    client = new ReputeAPI(process.env.REPUTEAPI_TEST_KEY);
  });

  test('should validate a real domain', async () => {
    const result = await client.check('google.com');

    expect(result).toHaveProperty('domain', 'google.com');
    expect(result).toHaveProperty('score');
    expect(result.score).toBeGreaterThanOrEqual(0);
    expect(result.score).toBeLessThanOrEqual(100);
  }, 10000); // 10 second timeout

  test('should handle rate limiting gracefully', async () => {
    // Make many rapid requests to trigger rate limiting
    const promises = Array(10).fill().map(() => 
      client.check('example.com').catch(err => err)
    );

    const results = await Promise.all(promises);

    // At least one should succeed, some might be rate limited
    const successes = results.filter(r => r.score !== undefined);
    const rateLimited = results.filter(r => r.code === 'RATE_LIMITED');

    expect(successes.length).toBeGreaterThan(0);
    console.log(`${successes.length} succeeded, ${rateLimited.length} rate limited`);
  }, 30000);
});

πŸ“š Next StepsΒΆ

Advanced TopicsΒΆ

CommunityΒΆ


Ready to build robust email validation into your Node.js applications? Start with our Quick Start Guide or explore real-world examples.