BLACK FRIDAY DEAL!
Get $100 OFF on lifetime access!

JavaScript Integration

Learn how to integrate PureSignup into your frontend JavaScript application for real-time email validation.

⚠️ Security Note: For production applications, always validate on the server-side as well. Frontend validation can be bypassed. This guide shows client-side validation for UX improvements only.

Basic Implementation

Here's a simple implementation that checks emails as users type into your signup form:

basic-validation.js
// Note: In production, call from your backend to keep API keys secure
const PureSignup_API_KEY = 'YOUR_API_KEY'; // DO NOT expose this in production

async function checkEmail(email) {
  try {
    const response = await fetch(
      'https://puresignup.com/api/v1/check?q=' + encodeURIComponent(email),
      {
        headers: {
          'Authorization': `Bearer ${PureSignup_API_KEY}`
        }
      }
    );

    if (!response.ok) {
      throw new Error('API request failed');
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Email check failed:', error);
    return null; // Fail open - allow signup on error
  }
}

// Usage example
const emailInput = document.getElementById('email');
const errorMessage = document.getElementById('email-error');

emailInput.addEventListener('blur', async () => {
  const email = emailInput.value;
  
  if (!email) return;

  const result = await checkEmail(email);
  
  if (result?.success && result.data.risk_level === 'high') {
    errorMessage.textContent = 'Please use a permanent email address';
    errorMessage.style.display = 'block';
    emailInput.classList.add('error');
  } else {
    errorMessage.style.display = 'none';
    emailInput.classList.remove('error');
  }
});

Production-Ready Setup

In production, proxy requests through your backend to keep your API key secure:

Frontend Code

frontend.js
async function checkEmail(email) {
  try {
    // Call your backend endpoint instead of PureSignup directly
    const response = await fetch('/api/check-email', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email })
    });

    if (!response.ok) {
      return null; // Fail open
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Email check failed:', error);
    return null;
  }
}

// Form submission handler
document.getElementById('signupForm').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const email = document.getElementById('email').value;
  const result = await checkEmail(email);
  
  if (result?.risk_level === 'high') {
    alert('Disposable email addresses are not allowed. Please use a permanent email.');
    return;
  }
  
  // Continue with signup
  e.target.submit();
});

Backend Proxy (Node.js Example)

backend-proxy.js
// Express.js endpoint
app.post('/api/check-email', async (req, res) => {
  const { email } = req.body;
  
  if (!email) {
    return res.status(400).json({ error: 'Email required' });
  }

  try {
    const response = await fetch(
      `https://puresignup.com/api/v1/check?q=${encodeURIComponent(email)}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.PureSignup_API_KEY}`
        }
      }
    );

    const data = await response.json();
    
    // Only send back what the frontend needs
    res.json({
      risk_level: data.data?.risk_level || 'low'
    });
  } catch (error) {
    console.error('PureSignup API error:', error);
    // Fail open - return low risk on error
    res.json({ risk_level: 'low' });
  }
});

Real-Time Validation with Debouncing

Check emails as users type, but use debouncing to avoid excessive API calls:

debounced-validation.js
// Debounce helper
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), wait);
  };
}

// Check email with visual feedback
async function validateEmailField(emailInput, errorElement) {
  const email = emailInput.value;
  
  if (!email || !email.includes('@')) {
    errorElement.style.display = 'none';
    return;
  }

  // Show loading state
  emailInput.classList.add('validating');
  
  const result = await checkEmail(email);
  
  // Remove loading state
  emailInput.classList.remove('validating');
  
  if (result?.risk_level === 'high') {
    errorElement.textContent = '⚠️ Disposable email detected. Please use a permanent address.';
    errorElement.style.display = 'block';
    errorElement.className = 'error-message error';
  } else if (result?.risk_level === 'low') {
    errorElement.textContent = '✓ Email looks good';
    errorElement.style.display = 'block';
    errorElement.className = 'error-message success';
  }
}

// Setup with debouncing (wait 500ms after user stops typing)
const emailInput = document.getElementById('email');
const errorElement = document.getElementById('email-feedback');

const debouncedValidate = debounce(() => {
  validateEmailField(emailInput, errorElement);
}, 500);

emailInput.addEventListener('input', debouncedValidate);

React Integration

Here's how to integrate with React using hooks:

EmailInput.jsx
import { useState, useEffect } from 'react';

function EmailInput({ onValidation }) {
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState(null); // null, 'checking', 'valid', 'invalid'
  const [message, setMessage] = useState('');

  useEffect(() => {
    if (!email || !email.includes('@')) {
      setStatus(null);
      return;
    }

    const timeoutId = setTimeout(async () => {
      setStatus('checking');
      
      try {
        const response = await fetch('/api/check-email', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ email })
        });
        
        const data = await response.json();
        
        if (data.risk_level === 'high') {
          setStatus('invalid');
          setMessage('Disposable email detected');
          onValidation(false);
        } else {
          setStatus('valid');
          setMessage('Email looks good');
          onValidation(true);
        }
      } catch (error) {
        setStatus('valid'); // Fail open
        onValidation(true);
      }
    }, 500); // Debounce 500ms

    return () => clearTimeout(timeoutId);
  }, [email, onValidation]);

  return (
    <div className="form-field">
      <label htmlFor="email">Email Address</label>
      <input
        type="email"
        id="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        className={`input ${status === 'invalid' ? 'error' : ''}`}
      />
      {status === 'checking' && (
        <span className="help-text">Checking...</span>
      )}
      {status === 'invalid' && (
        <span className="error-text">{message}</span>
      )}
      {status === 'valid' && (
        <span className="success-text">{message}</span>
      )}
    </div>
  );
}

export default EmailInput;

Vue.js Integration

EmailInput.vue
<template>
  <div class="form-field">
    <label for="email">Email Address</label>
    <input
      type="email"
      id="email"
      v-model="email"
      @input="debouncedCheck"
      :class="{ error: isInvalid }"
    />
    <span v-if="checking" class="help-text">Checking...</span>
    <span v-if="isInvalid" class="error-text">{{ message }}</span>
    <span v-if="isValid" class="success-text">{{ message }}</span>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  name: 'EmailInput',
  emits: ['validation'],
  setup(props, { emit }) {
    const email = ref('');
    const checking = ref(false);
    const isValid = ref(false);
    const isInvalid = ref(false);
    const message = ref('');
    let timeoutId = null;

    const checkEmail = async () => {
      if (!email.value || !email.value.includes('@')) {
        isValid.value = false;
        isInvalid.value = false;
        return;
      }

      checking.value = true;
      
      try {
        const response = await fetch('/api/check-email', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ email: email.value })
        });
        
        const data = await response.json();
        
        if (data.risk_level === 'high') {
          isInvalid.value = true;
          isValid.value = false;
          message.value = 'Disposable email detected';
          emit('validation', false);
        } else {
          isValid.value = true;
          isInvalid.value = false;
          message.value = 'Email looks good';
          emit('validation', true);
        }
      } catch (error) {
        isValid.value = true;
        isInvalid.value = false;
        emit('validation', true); // Fail open
      } finally {
        checking.value = false;
      }
    };

    const debouncedCheck = () => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(checkEmail, 500);
    };

    return {
      email,
      checking,
      isValid,
      isInvalid,
      message,
      debouncedCheck
    };
  }
};
</script>

Best Practices

  • Always validate server-side: Client-side validation is for UX only
  • Use debouncing: Wait 300-500ms after user stops typing before checking
  • Fail open: If API is unavailable, allow signup to proceed
  • Provide feedback: Show loading states and clear error messages
  • Cache results: Store results in memory to avoid duplicate checks
  • Proxy through backend: Never expose API keys in frontend code

Next Steps

© 2025 PureSignup. All rights reserved.