Node.js Email Validation

Complete guide to email validation in Node.js

What You'll Learn

  • Install and configure VerifyForge in Node.js
  • Validate emails in Express routes
  • Create reusable middleware
  • Handle validation errors
  • Implement bulk validation
  • Best practices for production

Prerequisites

  • Node.js 16+ installed
  • Basic knowledge of Node.js and Express
  • VerifyForge API key (get free key)

Installation

npm install @verifyforge/sdk express

Quick Start

import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: 'your_api_key' });

const result = await client.validate('user@example.com');

if (result.data.isValid) {
  console.log('✓ Email is valid!');
  console.log(`Reachability: ${result.data.reachability}`);
}

Tutorial 1: Express API Routes

// server.js
import express from 'express';
import { VerifyForge } from '@verifyforge/sdk';

const app = express();
const client = new VerifyForge({ apiKey: process.env.VERIFYFORGE_API_KEY });

app.use(express.json());

// Validate single email
app.post('/api/validate', async (req, res) => {
  const { email } = req.body;

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

  try {
    const result = await client.validate(email);

    res.json({
      success: true,
      data: {
        email: result.data.email,
        isValid: result.data.isValid,
        reachability: result.data.reachability,
        disposable: result.data.disposable,
        roleAccount: result.data.roleAccount,
        suggestion: result.data.suggestion,
      },
    });
  } catch (error) {
    console.error('Validation error:', error);
    res.status(500).json({ error: 'Validation failed' });
  }
});

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

  if (!Array.isArray(emails) || emails.length === 0) {
    return res.status(400).json({ error: 'Emails array is required' });
  }

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

  try {
    const result = await client.validateBulk(emails);

    res.json({
      success: true,
      data: result.data,
      summary: result.data.summary,
    });
  } catch (error) {
    console.error('Bulk validation error:', error);
    res.status(500).json({ error: 'Bulk validation failed' });
  }
});

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

Tutorial 2: Validation Middleware

// middleware/validateEmail.js
import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: process.env.VERIFYFORGE_API_KEY });

// Cache for validation results
const cache = new Map();
const CACHE_TTL = 3600000; // 1 hour

export const validateEmail = async (req, res, next) => {
  const email = req.body.email || req.query.email;

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

  try {
    // Check cache
    if (cache.has(email)) {
      const cached = cache.get(email);
      if (Date.now() - cached.timestamp < CACHE_TTL) {
        req.emailValidation = cached.data;
        return next();
      }
      cache.delete(email);
    }

    // Validate
    const result = await client.validate(email);

    if (!result.data.isValid) {
      return res.status(422).json({
        error: 'Invalid email address',
        suggestion: result.data.suggestion,
      });
    }

    if (result.data.disposable) {
      return res.status(422).json({
        error: 'Temporary email addresses are not allowed',
      });
    }

    // Cache result
    cache.set(email, {
      data: result.data,
      timestamp: Date.now(),
    });

    // Attach to request
    req.emailValidation = result.data;
    next();
  } catch (error) {
    console.error('Email validation error:', error);
    // Fail gracefully - allow request to continue
    next();
  }
};

Use in routes:

import { validateEmail } from './middleware/validateEmail.js';

app.post('/api/subscribe', validateEmail, async (req, res) => {
  const { email, name } = req.body;

  // Email is already validated by middleware
  const validation = req.emailValidation;

  // Save subscriber
  console.log('Saving subscriber:', { email, name, validation });

  res.json({ success: true, message: 'Subscribed successfully' });
});

Tutorial 3: Validation Service Class

// services/EmailValidationService.js
import { VerifyForge, AuthenticationError, InsufficientCreditsError } from '@verifyforge/sdk';

export class EmailValidationService {
  constructor(apiKey) {
    this.client = new VerifyForge({ apiKey });
    this.cache = new Map();
  }

  async validate(email) {
    // Check cache
    if (this.cache.has(email)) {
      const cached = this.cache.get(email);
      if (Date.now() - cached.timestamp < 3600000) {
        return cached.result;
      }
      this.cache.delete(email);
    }

    // Validate
    const result = await this.client.validate(email);

    // Cache result
    this.cache.set(email, {
      result: result.data,
      timestamp: Date.now(),
    });

    return result.data;
  }

  async validateBulk(emails) {
    const result = await this.client.validateBulk(emails);
    return result.data;
  }

  async isValid(email) {
    try {
      const result = await this.validate(email);
      return result.isValid;
    } catch (error) {
      console.error('Validation error:', error);
      return false;
    }
  }

  clearCache() {
    this.cache.clear();
  }
}

// Usage
const validationService = new EmailValidationService(process.env.VERIFYFORGE_API_KEY);
export default validationService;

Tutorial 4: Error Handling

// utils/errorHandler.js
import { AuthenticationError, InsufficientCreditsError, ValidationError } from '@verifyforge/sdk';

export const handleValidationError = (error, res) => {
  if (error instanceof AuthenticationError) {
    return res.status(401).json({
      error: 'Invalid API key',
    });
  }

  if (error instanceof InsufficientCreditsError) {
    return res.status(402).json({
      error: 'Insufficient credits',
    });
  }

  if (error instanceof ValidationError) {
    return res.status(400).json({
      error: error.message,
      details: error.details,
    });
  }

  console.error('Unexpected error:', error);
  return res.status(500).json({
    error: 'Internal server error',
  });
};

Use in routes:

import { handleValidationError } from './utils/errorHandler.js';

app.post('/api/validate', async (req, res) => {
  try {
    const result = await client.validate(req.body.email);
    res.json({ success: true, data: result.data });
  } catch (error) {
    handleValidationError(error, res);
  }
});

Tutorial 5: Rate Limiting

// middleware/rateLimiter.js
import rateLimit from 'express-rate-limit';

export const validationRateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many validation requests, please try again later',
  standardHeaders: true,
  legacyHeaders: false,
});

Apply to routes:

import { validationRateLimiter } from './middleware/rateLimiter.js';

app.post('/api/validate', validationRateLimiter, async (req, res) => {
  // ... validation logic
});

Best Practices

1. Environment Variables

# .env
VERIFYFORGE_API_KEY=your_api_key_here
PORT=3000
import 'dotenv/config';

const client = new VerifyForge({
  apiKey: process.env.VERIFYFORGE_API_KEY
});

2. Async Error Handling Wrapper

// utils/asyncHandler.js
export const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage
app.post('/api/validate', asyncHandler(async (req, res) => {
  const result = await client.validate(req.body.email);
  res.json({ success: true, data: result.data });
}));

3. Request Timeout

import timeout from 'connect-timeout';

app.use(timeout('30s'));
app.use((req, res, next) => {
  if (!req.timedout) next();
});

4. Logging

import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

// Log validation errors
try {
  const result = await client.validate(email);
  logger.info('Email validated', { email, isValid: result.data.isValid });
} catch (error) {
  logger.error('Validation failed', { email, error: error.message });
}

Complete Example: Newsletter API

// server.js
import express from 'express';
import { VerifyForge } from '@verifyforge/sdk';
import { validateEmail } from './middleware/validateEmail.js';
import { validationRateLimiter } from './middleware/rateLimiter.js';

const app = express();
const client = new VerifyForge({ apiKey: process.env.VERIFYFORGE_API_KEY });

app.use(express.json());

// In-memory storage (use database in production)
const subscribers = new Set();

app.post('/api/subscribe',
  validationRateLimiter,
  validateEmail,
  async (req, res) => {
    const { email, name } = req.body;

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

    // Check if already subscribed
    if (subscribers.has(email)) {
      return res.status(409).json({
        error: 'Email already subscribed',
      });
    }

    // Email is validated by middleware
    const validation = req.emailValidation;

    // Additional checks
    if (validation.reachability === 'risky') {
      return res.status(422).json({
        error: 'This email appears risky',
      });
    }

    // Add subscriber
    subscribers.add(email);

    res.json({
      success: true,
      message: 'Successfully subscribed',
      subscriber: { email, name },
    });
  }
);

app.get('/api/subscribers', (req, res) => {
  res.json({
    count: subscribers.size,
    subscribers: Array.from(subscribers),
  });
});

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

Testing

// test/validation.test.js
import { VerifyForge } from '@verifyforge/sdk';
import { expect } from 'chai';

describe('Email Validation', () => {
  const client = new VerifyForge({
    apiKey: process.env.VERIFYFORGE_API_KEY
  });

  it('should validate a valid email', async () => {
    const result = await client.validate('test@example.com');
    expect(result.data).to.have.property('isValid');
    expect(result.data.email).to.equal('test@example.com');
  });

  it('should detect disposable emails', async () => {
    const result = await client.validate('test@tempmail.com');
    expect(result.data.disposable).to.be.true;
  });

  it('should validate bulk emails', async () => {
    const emails = ['test1@example.com', 'test2@example.com'];
    const result = await client.validateBulk(emails);
    expect(result.data.results).to.have.lengthOf(2);
  });
});

Troubleshooting

Problem: Module not found errors

Solution: Ensure you're using ES modules. Add to package.json:

{
  "type": "module"
}

Problem: API key not found

Solution: Install dotenv and load it: import 'dotenv/config'

Problem: Timeout errors

Solution: Increase client timeout:

const client = new VerifyForge({
  apiKey: process.env.VERIFYFORGE_API_KEY,
  timeout: 60000 // 60 seconds
});

Next Steps