JavaScript Integration GuideΒΆ

Complete guide to integrating ReputeAPI with JavaScript and Node.js applications.


InstallationΒΆ

ReputeAPI works with any HTTP client. Here are the most popular options:

fetch (Built-in):

# No installation needed - built into modern browsers and Node.js 18+

axios:

npm install axios

node-fetch:

# For Node.js < 18
npm install node-fetch


Quick StartΒΆ

Browser (Fetch API)ΒΆ

const API_KEY = 'your-api-key-here';
const BASE_URL = 'https://api.reputeapi.com';

async function checkDomain(domain) {
  const response = await fetch(
    `${BASE_URL}/api/v1/check?domain=${domain}`,
    {
      headers: {
        'X-API-Key': API_KEY
      }
    }
  );

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const result = await response.json();
  return result;
}

// Usage
checkDomain('example.com')
  .then(result => {
    console.log(`Score: ${result.score}/100`);
    console.log(`Grade: ${result.grade}`);
  })
  .catch(error => {
    console.error('Error:', error);
  });

Node.js (Axios)ΒΆ

const axios = require('axios');

const API_KEY = process.env.REPUTE_API_KEY;
const BASE_URL = 'https://api.reputeapi.com';

async function checkDomain(domain) {
  try {
    const response = await axios.get(`${BASE_URL}/api/v1/check`, {
      params: { domain },
      headers: { 'X-API-Key': API_KEY }
    });

    return response.data;
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
checkDomain('example.com')
  .then(result => {
    console.log(`Score: ${result.score}/100`);
  });

Environment VariablesΒΆ

Create a .env file:

REPUTE_API_KEY=your-api-key-here
REPUTE_API_URL=https://api.reputeapi.com

Load it using dotenv:

npm install dotenv
require('dotenv').config();

const API_KEY = process.env.REPUTE_API_KEY;
const BASE_URL = process.env.REPUTE_API_URL || 'https://api.reputeapi.com';

Client ClassΒΆ

Create a reusable client for your application:

class ReputeAPIClient {
  constructor(apiKey, baseUrl = 'https://api.reputeapi.com') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  /**
   * Run complete mailflow security check
   * @param {string} domain - Domain to check
   * @param {Object} options - Optional parameters
   * @returns {Promise<Object>} Validation results
   */
  async checkDomain(domain, options = {}) {
    const params = new URLSearchParams({
      domain,
      ...options
    });

    if (options.selectors && Array.isArray(options.selectors)) {
      params.set('selectors', options.selectors.join(','));
    }

    const response = await fetch(
      `${this.baseUrl}/api/v1/check?${params}`,
      {
        headers: { 'X-API-Key': this.apiKey }
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Get quick score check (faster)
   * @param {string} domain - Domain to check
   * @param {boolean} refresh - Bypass cache
   * @returns {Promise<Object>} Score and top issues
   */
  async getScore(domain, refresh = false) {
    const params = new URLSearchParams({ domain, refresh });

    const response = await fetch(
      `${this.baseUrl}/api/v1/score?${params}`,
      {
        headers: { 'X-API-Key': this.apiKey }
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Get prioritized recommendations
   * @param {string} domain - Domain to get recommendations for
   * @param {string} severity - Filter by severity
   * @returns {Promise<Object>} Recommendations
   */
  async getRecommendations(domain, severity = null) {
    const body = { domain };
    if (severity) body.severity = severity;

    const response = await fetch(
      `${this.baseUrl}/api/v1/recommendations`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Get historical validation data
   * @param {string} domain - Domain to get history for
   * @param {Object} options - Date range options
   * @returns {Promise<Object>} Historical snapshots
   */
  async getHistory(domain, options = {}) {
    const params = new URLSearchParams({ domain });

    if (options.days) {
      params.set('days', options.days);
    } else if (options.startDate && options.endDate) {
      params.set('start_date', options.startDate.toISOString());
      params.set('end_date', options.endDate.toISOString());
    } else {
      params.set('days', 30);
    }

    const response = await fetch(
      `${this.baseUrl}/api/v1/history?${params}`,
      {
        headers: { 'X-API-Key': this.apiKey }
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Get API usage statistics
   * @returns {Promise<Object>} Usage stats
   */
  async getUsage() {
    const response = await fetch(
      `${this.baseUrl}/api/v1/usage`,
      {
        headers: { 'X-API-Key': this.apiKey }
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Validate multiple domains (legacy endpoint)
   * @param {string[]} domains - List of domains
   * @returns {Promise<Object>} Results for all domains
   */
  async bulkValidate(domains) {
    const response = await fetch(
      `${this.baseUrl}/v1/bulk-validate`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ domains })
      }
    );

    if (!response.ok) {
      throw await this._handleError(response);
    }

    return response.json();
  }

  /**
   * Handle API errors
   * @private
   */
  async _handleError(response) {
    let errorData;
    try {
      errorData = await response.json();
    } catch {
      errorData = { message: response.statusText };
    }

    const error = new Error(errorData.message || 'API request failed');
    error.status = response.status;
    error.data = errorData;

    return error;
  }
}

// Export for Node.js
if (typeof module !== 'undefined' && module.exports) {
  module.exports = ReputeAPIClient;
}

// Usage
const client = new ReputeAPIClient(API_KEY);

client.checkDomain('example.com')
  .then(result => {
    console.log(`Score: ${result.score}/100`);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

Error HandlingΒΆ

Comprehensive Error HandlingΒΆ

class ReputeAPIError extends Error {
  constructor(message, status, data) {
    super(message);
    this.name = 'ReputeAPIError';
    this.status = status;
    this.data = data;
  }
}

class AuthenticationError extends ReputeAPIError {
  constructor(message, data) {
    super(message, 401, data);
    this.name = 'AuthenticationError';
  }
}

class RateLimitError extends ReputeAPIError {
  constructor(message, retryAfter, data) {
    super(message, 429, data);
    this.name = 'RateLimitError';
    this.retryAfter = retryAfter;
  }
}

class ValidationError extends ReputeAPIError {
  constructor(message, data) {
    super(message, 400, data);
    this.name = 'ValidationError';
  }
}

async function checkDomainSafe(domain) {
  try {
    const response = await fetch(
      `${BASE_URL}/api/v1/check?domain=${domain}`,
      {
        headers: { 'X-API-Key': API_KEY },
        signal: AbortSignal.timeout(10000) // 10 second timeout
      }
    );

    // Handle specific error codes
    if (response.status === 401) {
      throw new AuthenticationError('Invalid or missing API key');
    }

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 60;
      throw new RateLimitError(
        'Rate limit exceeded',
        parseInt(retryAfter)
      );
    }

    if (response.status === 400) {
      const errorData = await response.json();
      throw new ValidationError(errorData.message);
    }

    if (response.status >= 500) {
      throw new ReputeAPIError(
        `Server error: ${response.status}`,
        response.status
      );
    }

    if (!response.ok) {
      throw new ReputeAPIError(
        `Request failed: ${response.status}`,
        response.status
      );
    }

    return await response.json();

  } catch (error) {
    if (error instanceof AuthenticationError) {
      console.error('Authentication error:', error.message);
      console.error('Please check your API key');
    } else if (error instanceof RateLimitError) {
      console.error(`Rate limit exceeded. Retry after ${error.retryAfter} seconds`);
    } else if (error instanceof ValidationError) {
      console.error('Validation error:', error.message);
    } else if (error.name === 'AbortError') {
      console.error('Request timed out');
    } else if (error.name === 'TypeError') {
      console.error('Network error:', error.message);
    } else {
      console.error('Error:', error.message);
    }

    return null;
  }
}

// Usage
const result = await checkDomainSafe('example.com');
if (result) {
  console.log(`Score: ${result.score}/100`);
}

Retry Logic with Exponential BackoffΒΆ

/**
 * Retry function with exponential backoff
 * @param {Function} fn - Async function to retry
 * @param {Object} options - Retry options
 * @returns {Promise} Result of function
 */
async function retryWithBackoff(
  fn,
  options = {}
) {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 60000
  } = options;

  let retries = 0;
  let delay = baseDelay;

  while (retries < maxRetries) {
    try {
      return await fn();

    } catch (error) {
      retries++;

      // Don't retry on authentication or validation errors
      if (error instanceof AuthenticationError ||
          error instanceof ValidationError) {
        throw error;
      }

      // Handle rate limit with server-provided delay
      if (error instanceof RateLimitError) {
        delay = error.retryAfter * 1000;
      }

      if (retries >= maxRetries) {
        throw new Error(`Max retries (${maxRetries}) exceeded: ${error.message}`);
      }

      console.log(`Retry ${retries}/${maxRetries} after ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));

      // Exponential backoff
      delay = Math.min(delay * 2, maxDelay);
    }
  }
}

// Usage
const result = await retryWithBackoff(
  () => checkDomainSafe('example.com'),
  { maxRetries: 3, baseDelay: 2000 }
);

Axios-based ClientΒΆ

Alternative implementation using Axios:

const axios = require('axios');

class AxiosReputeAPIClient {
  constructor(apiKey, baseUrl = 'https://api.reputeapi.com') {
    this.client = axios.create({
      baseURL: baseUrl,
      headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
      },
      timeout: 10000
    });

    // Add response interceptor for error handling
    this.client.interceptors.response.use(
      response => response,
      error => this._handleError(error)
    );
  }

  async checkDomain(domain, options = {}) {
    const response = await this.client.get('/api/v1/check', {
      params: {
        domain,
        ...options,
        selectors: options.selectors?.join(',')
      }
    });
    return response.data;
  }

  async getScore(domain, refresh = false) {
    const response = await this.client.get('/api/v1/score', {
      params: { domain, refresh }
    });
    return response.data;
  }

  async getRecommendations(domain, severity = null) {
    const response = await this.client.post('/api/v1/recommendations', {
      domain,
      ...(severity && { severity })
    });
    return response.data;
  }

  async getHistory(domain, options = {}) {
    const params = { domain };

    if (options.days) {
      params.days = options.days;
    } else if (options.startDate && options.endDate) {
      params.start_date = options.startDate.toISOString();
      params.end_date = options.endDate.toISOString();
    } else {
      params.days = 30;
    }

    const response = await this.client.get('/api/v1/history', { params });
    return response.data;
  }

  async getUsage() {
    const response = await this.client.get('/api/v1/usage');
    return response.data;
  }

  async bulkValidate(domains) {
    const response = await this.client.post('/v1/bulk-validate', {
      domains
    });
    return response.data;
  }

  _handleError(error) {
    if (error.response) {
      // Server responded with error status
      const { status, data } = error.response;

      if (status === 401) {
        throw new AuthenticationError(
          data.message || 'Invalid API key',
          data
        );
      }

      if (status === 429) {
        const retryAfter = error.response.headers['retry-after'] || 60;
        throw new RateLimitError(
          data.message || 'Rate limit exceeded',
          parseInt(retryAfter),
          data
        );
      }

      if (status === 400) {
        throw new ValidationError(
          data.message || 'Invalid request',
          data
        );
      }

      throw new ReputeAPIError(
        data.message || 'API request failed',
        status,
        data
      );

    } else if (error.request) {
      // Request made but no response
      throw new Error('No response from server');

    } else {
      // Error in request setup
      throw error;
    }
  }
}

module.exports = AxiosReputeAPIClient;

Common Use CasesΒΆ

1. React Component IntegrationΒΆ

import React, { useState, useEffect } from 'react';

function DomainSecurityScore({ domain }) {
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const checkDomain = async () => {
      setLoading(true);
      setError(null);

      try {
        const client = new ReputeAPIClient(process.env.REACT_APP_API_KEY);
        const data = await client.getScore(domain);
        setResult(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    if (domain) {
      checkDomain();
    }
  }, [domain]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!result) return null;

  return (
    <div className="domain-score">
      <h2>{domain}</h2>
      <div className="score">
        <span className="score-value">{result.score}</span>
        <span className="score-max">/100</span>
      </div>
      <div className={`grade grade-${result.grade.toLowerCase()}`}>
        {result.grade}
      </div>
      {result.top_issues && result.top_issues.length > 0 && (
        <div className="issues">
          <h3>Top Issues:</h3>
          <ul>
            {result.top_issues.map((issue, index) => (
              <li key={index} className={`severity-${issue.severity}`}>
                {issue.message}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

export default DomainSecurityScore;

2. Express.js API ServerΒΆ

const express = require('express');
const ReputeAPIClient = require('./ReputeAPIClient');

const app = express();
const client = new ReputeAPIClient(process.env.REPUTE_API_KEY);

app.use(express.json());

// Check domain endpoint
app.get('/api/check/:domain', async (req, res) => {
  try {
    const { domain } = req.params;
    const result = await client.checkDomain(domain);
    res.json(result);
  } catch (error) {
    res.status(error.status || 500).json({
      error: error.message
    });
  }
});

// Get score endpoint
app.get('/api/score/:domain', async (req, res) => {
  try {
    const { domain } = req.params;
    const result = await client.getScore(domain);
    res.json(result);
  } catch (error) {
    res.status(error.status || 500).json({
      error: error.message
    });
  }
});

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

    if (!Array.isArray(domains)) {
      return res.status(400).json({
        error: 'domains must be an array'
      });
    }

    // Check domains concurrently
    const results = await Promise.allSettled(
      domains.map(domain => client.getScore(domain))
    );

    const response = results.map((result, index) => ({
      domain: domains[index],
      ...(result.status === 'fulfilled'
        ? { success: true, data: result.value }
        : { success: false, error: result.reason.message }
      )
    }));

    res.json({ results: response });

  } catch (error) {
    res.status(500).json({
      error: error.message
    });
  }
});

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

3. Batch Domain Checker (Node.js)ΒΆ

const fs = require('fs');
const { createObjectCsvWriter } = require('csv-writer');

async function auditDomainsBatch(domains, outputFile = 'audit_results.csv') {
  const client = new ReputeAPIClient(process.env.REPUTE_API_KEY);
  const results = [];

  console.log(`Auditing ${domains.length} domains...`);

  for (let i = 0; i < domains.length; i++) {
    const domain = domains[i];
    process.stdout.write(`[${i + 1}/${domains.length}] ${domain}... `);

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

      const auditResult = {
        domain,
        score: result.score,
        grade: result.grade,
        timestamp: new Date().toISOString(),
        status: 'success'
      };

      // Count issues by severity
      const topIssues = result.top_issues || [];
      auditResult.critical_count = topIssues.filter(
        i => i.severity === 'critical'
      ).length;
      auditResult.high_count = topIssues.filter(
        i => i.severity === 'high'
      ).length;

      results.push(auditResult);
      console.log(`Score: ${result.score}/100`);

    } catch (error) {
      results.push({
        domain,
        score: 0,
        grade: 'Error',
        timestamp: new Date().toISOString(),
        status: `error: ${error.message}`,
        critical_count: 0,
        high_count: 0
      });
      console.log(`Error: ${error.message}`);
    }

    // Rate limiting: wait between requests
    if (i < domains.length - 1) {
      await new Promise(resolve => setTimeout(resolve, 500));
    }
  }

  // Save to CSV
  const csvWriter = createObjectCsvWriter({
    path: outputFile,
    header: [
      { id: 'domain', title: 'Domain' },
      { id: 'score', title: 'Score' },
      { id: 'grade', title: 'Grade' },
      { id: 'critical_count', title: 'Critical Issues' },
      { id: 'high_count', title: 'High Issues' },
      { id: 'status', title: 'Status' },
      { id: 'timestamp', title: 'Timestamp' }
    ]
  });

  await csvWriter.writeRecords(results);
  console.log(`\nResults saved to ${outputFile}`);

  // Summary
  const totalDomains = results.length;
  const passingDomains = results.filter(r => r.score >= 80).length;
  console.log(`\nSummary: ${passingDomains}/${totalDomains} domains passing (80+)`);

  return results;
}

// Usage
const domains = ['example.com', 'test.com', 'demo.com'];
auditDomainsBatch(domains);

4. Monitoring Script with AlertsΒΆ

const schedule = require('node-schedule');
const nodemailer = require('nodemailer');

class DomainMonitor {
  constructor(apiKey, alertThreshold = 70) {
    this.client = new ReputeAPIClient(apiKey);
    this.alertThreshold = alertThreshold;
    this.previousScores = new Map();

    // Configure email alerts
    this.mailer = nodemailer.createTransport({
      service: 'gmail',
      auth: {
        user: process.env.EMAIL_USER,
        pass: process.env.EMAIL_PASS
      }
    });
  }

  async checkDomain(domain) {
    try {
      const result = await this.client.getScore(domain);
      const currentScore = result.score;
      const previousScore = this.previousScores.get(domain);

      console.log(`[${new Date().toISOString()}] ${domain}: ${currentScore}/100`);

      // Alert if score below threshold
      if (currentScore < this.alertThreshold) {
        await this.sendAlert(
          domain,
          `Score below threshold: ${currentScore} < ${this.alertThreshold}`
        );
      }

      // Alert if score dropped significantly
      if (previousScore && currentScore < previousScore - 10) {
        await this.sendAlert(
          domain,
          `Score dropped from ${previousScore} to ${currentScore}`
        );
      }

      this.previousScores.set(domain, currentScore);

    } catch (error) {
      console.error(`Error checking ${domain}:`, error.message);
      await this.sendAlert(domain, `Check failed: ${error.message}`);
    }
  }

  async sendAlert(domain, message) {
    console.log(`ALERT for ${domain}: ${message}`);

    // Send email alert
    try {
      await this.mailer.sendMail({
        from: process.env.EMAIL_USER,
        to: process.env.ALERT_EMAIL,
        subject: `ReputeAPI Alert: ${domain}`,
        text: `Alert for ${domain}:\n\n${message}\n\nTime: ${new Date().toISOString()}`
      });
    } catch (error) {
      console.error('Failed to send email alert:', error.message);
    }
  }

  async monitorDomains(domains) {
    for (const domain of domains) {
      await this.checkDomain(domain);
    }
  }

  scheduleMonitoring(domains, cronExpression = '0 * * * *') {
    // Default: every hour
    schedule.scheduleJob(cronExpression, async () => {
      await this.monitorDomains(domains);
    });

    console.log(`Monitoring scheduled: ${cronExpression}`);
    console.log(`Watching domains: ${domains.join(', ')}`);
  }
}

// Usage
const monitor = new DomainMonitor(process.env.REPUTE_API_KEY, 75);
const domains = ['example.com', 'test.com'];

// Run initial check
monitor.monitorDomains(domains);

// Schedule hourly checks
monitor.scheduleMonitoring(domains, '0 * * * *');

5. Vue.js Dashboard ComponentΒΆ

<template>
  <div class="domain-dashboard">
    <h1>Domain Security Dashboard</h1>

    <div class="domain-input">
      <input
        v-model="newDomain"
        @keyup.enter="addDomain"
        placeholder="Enter domain to check"
      />
      <button @click="addDomain">Add Domain</button>
    </div>

    <div class="domains-grid">
      <div
        v-for="domain in domains"
        :key="domain.name"
        class="domain-card"
        :class="`grade-${domain.grade?.toLowerCase()}`"
      >
        <h3>{{ domain.name }}</h3>

        <div v-if="domain.loading" class="loading">
          Loading...
        </div>

        <div v-else-if="domain.error" class="error">
          Error: {{ domain.error }}
        </div>

        <div v-else-if="domain.score !== null" class="domain-info">
          <div class="score">{{ domain.score }}/100</div>
          <div class="grade">{{ domain.grade }}</div>

          <div v-if="domain.topIssues?.length" class="issues">
            <h4>Top Issues:</h4>
            <ul>
              <li
                v-for="(issue, index) in domain.topIssues"
                :key="index"
                :class="`severity-${issue.severity}`"
              >
                {{ issue.message }}
              </li>
            </ul>
          </div>
        </div>

        <button @click="refreshDomain(domain.name)" class="refresh-btn">
          Refresh
        </button>
        <button @click="removeDomain(domain.name)" class="remove-btn">
          Remove
        </button>
      </div>
    </div>
  </div>
</template>

<script>
import ReputeAPIClient from './ReputeAPIClient';

export default {
  name: 'DomainDashboard',

  data() {
    return {
      domains: [],
      newDomain: '',
      client: null
    };
  },

  created() {
    this.client = new ReputeAPIClient(process.env.VUE_APP_API_KEY);
  },

  methods: {
    async addDomain() {
      const domain = this.newDomain.trim();
      if (!domain) return;

      // Check if domain already exists
      if (this.domains.find(d => d.name === domain)) {
        alert('Domain already added');
        return;
      }

      this.domains.push({
        name: domain,
        loading: true,
        score: null,
        grade: null,
        topIssues: [],
        error: null
      });

      this.newDomain = '';
      await this.checkDomain(domain);
    },

    async checkDomain(domainName) {
      const domain = this.domains.find(d => d.name === domainName);
      if (!domain) return;

      domain.loading = true;
      domain.error = null;

      try {
        const result = await this.client.getScore(domainName);
        domain.score = result.score;
        domain.grade = result.grade;
        domain.topIssues = result.top_issues || [];
      } catch (error) {
        domain.error = error.message;
      } finally {
        domain.loading = false;
      }
    },

    async refreshDomain(domainName) {
      await this.checkDomain(domainName);
    },

    removeDomain(domainName) {
      this.domains = this.domains.filter(d => d.name !== domainName);
    }
  }
};
</script>

<style scoped>
.domain-dashboard {
  padding: 20px;
}

.domains-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
  margin-top: 20px;
}

.domain-card {
  border: 2px solid #ddd;
  border-radius: 8px;
  padding: 20px;
  background: white;
}

.domain-card.grade-excellent {
  border-color: #22c55e;
}

.domain-card.grade-good {
  border-color: #3b82f6;
}

.domain-card.grade-fair {
  border-color: #f59e0b;
}

.domain-card.grade-poor {
  border-color: #ef4444;
}

.score {
  font-size: 48px;
  font-weight: bold;
  text-align: center;
}

.grade {
  text-align: center;
  font-size: 24px;
  margin: 10px 0;
}
</style>

Performance TipsΒΆ

1. Request CachingΒΆ

class CachedReputeAPIClient extends ReputeAPIClient {
  constructor(apiKey, baseUrl, cacheTtl = 900000) { // 15 minutes
    super(apiKey, baseUrl);
    this.cache = new Map();
    this.cacheTtl = cacheTtl;
  }

  _getCacheKey(endpoint, params) {
    return `${endpoint}:${JSON.stringify(params)}`;
  }

  _getFromCache(key) {
    const cached = this.cache.get(key);
    if (cached && Date.now() - cached.timestamp < this.cacheTtl) {
      return cached.data;
    }
    return null;
  }

  _setCache(key, data) {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
  }

  async checkDomain(domain, options = {}) {
    const cacheKey = this._getCacheKey('check', { domain, ...options });

    // Try cache first
    if (!options.refresh) {
      const cached = this._getFromCache(cacheKey);
      if (cached) return cached;
    }

    // Fetch from API
    const result = await super.checkDomain(domain, options);

    // Cache result
    this._setCache(cacheKey, result);

    return result;
  }
}

2. Concurrent Requests with Rate LimitingΒΆ

class RateLimitedQueue {
  constructor(maxConcurrent = 5, minInterval = 200) {
    this.maxConcurrent = maxConcurrent;
    this.minInterval = minInterval;
    this.queue = [];
    this.running = 0;
    this.lastRun = 0;
  }

  async add(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this._process();
    });
  }

  async _process() {
    if (this.running >= this.maxConcurrent || this.queue.length === 0) {
      return;
    }

    // Rate limiting: ensure minimum interval between requests
    const now = Date.now();
    const timeSinceLastRun = now - this.lastRun;
    if (timeSinceLastRun < this.minInterval) {
      setTimeout(() => this._process(), this.minInterval - timeSinceLastRun);
      return;
    }

    const { fn, resolve, reject } = this.queue.shift();
    this.running++;
    this.lastRun = Date.now();

    try {
      const result = await fn();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.running--;
      this._process();
    }
  }
}

// Usage
const queue = new RateLimitedQueue(5, 200); // Max 5 concurrent, 200ms between
const client = new ReputeAPIClient(API_KEY);

async function checkDomainsWithRateLimit(domains) {
  const results = await Promise.all(
    domains.map(domain =>
      queue.add(() => client.getScore(domain))
    )
  );
  return results;
}

TestingΒΆ

Jest Unit TestsΒΆ

const ReputeAPIClient = require('./ReputeAPIClient');

// Mock fetch
global.fetch = jest.fn();

describe('ReputeAPIClient', () => {
  let client;

  beforeEach(() => {
    client = new ReputeAPIClient('test-api-key');
    fetch.mockClear();
  });

  test('checkDomain returns result', async () => {
    const mockResponse = {
      domain: 'example.com',
      score: 85,
      grade: 'Good'
    };

    fetch.mockResolvedValueOnce({
      ok: true,
      json: async () => mockResponse
    });

    const result = await client.checkDomain('example.com');

    expect(result).toEqual(mockResponse);
    expect(fetch).toHaveBeenCalledWith(
      expect.stringContaining('/api/v1/check?domain=example.com'),
      expect.objectContaining({
        headers: { 'X-API-Key': 'test-api-key' }
      })
    );
  });

  test('handles authentication error', async () => {
    fetch.mockResolvedValueOnce({
      ok: false,
      status: 401,
      json: async () => ({ message: 'Invalid API key' })
    });

    await expect(client.checkDomain('example.com'))
      .rejects
      .toThrow('Invalid API key');
  });

  test('handles rate limit error', async () => {
    fetch.mockResolvedValueOnce({
      ok: false,
      status: 429,
      headers: {
        get: () => '60'
      },
      json: async () => ({ message: 'Rate limit exceeded' })
    });

    await expect(client.checkDomain('example.com'))
      .rejects
      .toThrow('Rate limit exceeded');
  });
});

Next StepsΒΆ


Additional ResourcesΒΆ


SupportΒΆ

Need help with JavaScript integration?