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ΒΆ
Related GuidesΒΆ
- API Reference - Complete endpoint documentation
- Integration Patterns - Best practices
- Error Handling - Common issues and solutions
Advanced TopicsΒΆ
- Webhook Integration - Real-time notifications
- Caching Strategies - Optimize performance
- Monitoring Setup - Track your usage
CommunityΒΆ
- GitHub Repository - SDK source code
- Discord Community - Developer discussions
- Stack Overflow - Q&A
Ready to build robust email validation into your Node.js applications? Start with our Quick Start Guide or explore real-world examples.