ãã©ãŒã ã®é¢è±ã¯äŒæ¥ã«æ¯å¹Žæ°ååãã«ã®ã³ã¹ãããããããŠãããç¡å¹ãªã¡ãŒã«ã¢ãã¬ã¹ã¯ãã®äž»ãªåå ã®äžã€ã§ãããŠãŒã¶ãŒã誀ã£ãã¡ãŒã«ã¢ãã¬ã¹ãå ¥åãããã©ãŒã ãéä¿¡ããåŸã«ãšã©ãŒãçºèŠããå Žåããã©ã¹ãã¬ãŒã·ã§ã³ãé¢è±ã«ã€ãªãããŸãããªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ããŠãŒã¶ãŒãå ¥åããéã«ã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒãããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãšããŒã¿å質ã®äž¡æ¹ãåäžãããå³åº§ã®ãã£ãŒãããã¯ãæäŸããããšã§ããã®åé¡ã解決ããŸãã
ãã®å æ¬çãªã¬ã€ãã§ã¯ãåºæ¬çãªã¯ã©ã€ã¢ã³ãåŽã®æ€èšŒãããç¡å¹ãªã¡ãŒã«ã¢ãã¬ã¹ãäœ¿ãæšãŠã¡ãŒã«ã¢ãã¬ã¹ããªã¹ã¯ã®é«ãã¡ãŒã«ã¢ãã¬ã¹ãããŒã¿ããŒã¹ã«å ¥ãåã«æ€åºããé«åºŠãª API ããŒã¹ã®æ€èšŒã·ã¹ãã ãŸã§ããªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã®å®è£ ãæ¢ããŸãã
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã®çè§£
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ããã©ãŒã éä¿¡ããããåŠçãåŸ ã€ã®ã§ã¯ãªãããŠãŒã¶ãŒããã©ãŒã ãšå¯Ÿè©±ããéã«å³åº§ã«ã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒããŸãããã®ã¢ãããŒãã¯ãã¡ãŒã«ã®æå¹æ§ã«ã€ããŠå³åº§ã®ãã£ãŒãããã¯ãæäŸããããã«ãè€æ°ã®æ€èšŒæè¡ãçµã¿åãããŠããŸãã
ãªã¢ã«ã¿ã€ã æ€èšŒãšãããåŠçã®éã
åŸæ¥ã®ãããã¡ãŒã«æ€èšŒã¯ãåéåŸã«ã¡ãŒã«ãªã¹ããåŠçãããããããã€ãã®åé¡ãçºçããŸããç¡å¹ãªã¡ãŒã«ã¯ãã§ã«ããŒã¿ããŒã¹ã«å ¥ã£ãŠããããŠãŒã¶ãŒã¯ä¿®æ£ã®æ©äŒãªãã«æ ãå®äºãããªã¹ãã®ã¯ãªãŒãã³ã°ã¯å¥ã®éçšã¿ã¹ã¯ã«ãªããŸãã
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ç°ãªãåäœãããŸããã¡ãŒã«ããªããŒã¿ãŒã¯å ¥åæç¹ã§ã¢ãã¬ã¹ããã§ãã¯ããç¡å¹ãªããŒã¿ãã·ã¹ãã ã«å°éããããšãé²ããŸãããŠãŒã¶ãŒã¯å³åº§ã®ãã£ãŒãããã¯ãåãåãããã©ãŒã ã«é¢äžããŠããéã«ã¿ã€ããã¹ãä¿®æ£ãããã代æ¿ã®ã¢ãã¬ã¹ãæäŸãããããããšãã§ããŸãã
æ€èšŒãã€ãã©ã€ã³
å æ¬çãªãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã·ã¹ãã ã¯ãè€æ°ã®ãã§ãã¯ãé æ¬¡å®è¡ããŸãïŒ
æ§ææ€èšŒïŒæåã®å±€ã¯ãã¡ãŒã«ãé©åãªãã©ãŒãããèŠåã«åŸã£ãŠãããã©ããããã§ãã¯ããŸããããã«ã¯ã@ èšå·ã®ååšã®æ€èšŒãããŒã«ã«éšåïŒ@ ã®åïŒãšãã¡ã€ã³éšåïŒ@ ã®åŸïŒã®æ€èšŒãç¡å¹ãªæåãååšããªãããšã®ç¢ºèªãå«ãŸããŸãã
ãã¡ã€ã³æ€èšŒïŒã·ã¹ãã ã¯ããã¡ã€ã³ãååšããDNS ã¬ã³ãŒããã¯ãšãªããŠã¡ãŒã«ãåä¿¡ã§ãããã©ããããã§ãã¯ããŸããããã«ããããgmial.comãã®ãããªã¿ã€ããã¹ãå®å šã«æ¶ç©ºã®ãã¡ã€ã³ãæ€åºããŸãã
MX ã¬ã³ãŒããã§ãã¯ïŒã¡ãŒã«äº€æã¬ã³ãŒãã¯ããã¡ã€ã³ã®ã¡ãŒã«ãåŠçãããµãŒããŒã瀺ããŸããMX ã¬ã³ãŒãã®ãªããã¡ã€ã³ã¯ã¡ãŒã«ãåä¿¡ã§ããªãããããããã®ãã¡ã€ã³ã®ã¢ãã¬ã¹ã¯ç¡å¹ã§ãã
SMTP æ€èšŒïŒæã培åºçãªãã§ãã¯ã¯ãå®å ã¡ãŒã«ãµãŒããŒã«æ¥ç¶ããå®éã«ã¡ãŒã«ãéä¿¡ããããšãªãã¡ãŒã«ããã¯ã¹ãååšããããšãæ€èšŒããŸããããã«ããããã¡ã€ã³ã¯æå¹ã ãç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšããªãã¢ãã¬ã¹ãæ€åºããŸãã
ãªã¹ã¯è©äŸ¡ïŒé«åºŠãªã¡ãŒã«æ€èšŒãµãŒãã¹ã¯ãã¢ãã¬ã¹ãäœ¿ãæšãŠãã圹å²ããŒã¹ããæ¢ç¥ã®ã¹ãã ãã¿ãŒã³ã«é¢é£ä»ããããŠããããªã©ã®è¿œå èŠå ãåæããŸãã
ã¯ã©ã€ã¢ã³ãåŽæ€èšŒã®å®è£
ã¯ã©ã€ã¢ã³ãåŽæ€èšŒã¯ãæåã®é²åŸ¡ç·ãšå³åº§ã®ãŠãŒã¶ãŒãã£ãŒãããã¯ãæäŸããŸããåç¬ã§ã¯äžååã§ããããµãŒããŒãžã®ã©ãŠã³ãããªãããå¿ èŠãšããã«æãããªãšã©ãŒãæ€åºããŸãã
HTML5 ã¡ãŒã«æ€èšŒ
ææ°ã®ãã©ãŠã¶ã«ã¯ãHTML5 ã¡ãŒã«å ¥åã¿ã€ããéããçµã¿èŸŒã¿ã®ã¡ãŒã«æ€èšŒãå«ãŸããŠããŸãïŒ
<form id="signup-form">
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
required
placeholder="you@example.com"
>
<span class="error-message"></span>
<button type="submit">Sign Up</button>
</form>
type="email" 屿§ã¯ãåºæ¬çãªã¡ãŒã«ãã©ãŒãããããã§ãã¯ãããã©ãŠã¶æ€èšŒãããªã¬ãŒããŸãããã ãããã©ãŠã¶ã®æ€èšŒã¯å¯å®¹ã§ãæè¡çã«ç¡å¹ãªå€ãã®ã¢ãã¬ã¹ãåãå
¥ããŸãã
匷åããã JavaScript æ€èšŒ
ãã培åºçãªã¯ã©ã€ã¢ã³ãåŽãã§ãã¯ã®ããã«ãã«ã¹ã¿ã JavaScript æ€èšŒãå®è£ ããŸãïŒ
class EmailValidator {
constructor(inputElement) {
this.input = inputElement;
this.errorElement = inputElement.nextElementSibling;
this.setupListeners();
}
setupListeners() {
this.input.addEventListener('blur', () => this.validate());
this.input.addEventListener('input', () => this.clearError());
}
validate() {
const email = this.input.value.trim();
if (!email) {
return this.showError('Email address is required');
}
if (!this.isValidFormat(email)) {
return this.showError('Please enter a valid email address');
}
if (this.hasCommonTypo(email)) {
return this.showError(this.getTypoSuggestion(email));
}
this.showSuccess();
return true;
}
isValidFormat(email) {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email);
}
hasCommonTypo(email) {
const domain = email.split('@')[1]?.toLowerCase();
const typos = {
'gmial.com': 'gmail.com',
'gmal.com': 'gmail.com',
'gamil.com': 'gmail.com',
'hotmal.com': 'hotmail.com',
'outlok.com': 'outlook.com',
'yahooo.com': 'yahoo.com'
};
return typos.hasOwnProperty(domain);
}
getTypoSuggestion(email) {
const [local, domain] = email.split('@');
const corrections = {
'gmial.com': 'gmail.com',
'gmal.com': 'gmail.com',
'gamil.com': 'gmail.com'
};
const corrected = corrections[domain.toLowerCase()];
return `Did you mean ${local}@${corrected}?`;
}
showError(message) {
this.input.classList.add('invalid');
this.input.classList.remove('valid');
this.errorElement.textContent = message;
this.errorElement.classList.add('visible');
return false;
}
showSuccess() {
this.input.classList.add('valid');
this.input.classList.remove('invalid');
this.errorElement.classList.remove('visible');
}
clearError() {
this.errorElement.classList.remove('visible');
this.input.classList.remove('invalid', 'valid');
}
}
// Initialize validator
const emailInput = document.getElementById('email');
const validator = new EmailValidator(emailInput);
èŠèŠçãã£ãŒãããã¯ã®ããã® CSS
æ€èšŒç¶æ ã®æç¢ºãªèŠèŠçã€ã³ãžã±ãŒã¿ãŒãæäŸããŸãïŒ
.form-group input {
padding: 12px 16px;
border: 2px solid #e0e0e0;
border-radius: 8px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.form-group input:focus {
outline: none;
border-color: #2196f3;
box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);
}
.form-group input.valid {
border-color: #4caf50;
background-image: url("data:image/svg+xml,...");
background-repeat: no-repeat;
background-position: right 12px center;
}
.form-group input.invalid {
border-color: #f44336;
}
.error-message {
display: block;
color: #f44336;
font-size: 14px;
margin-top: 4px;
opacity: 0;
transform: translateY(-4px);
transition: opacity 0.2s, transform 0.2s;
}
.error-message.visible {
opacity: 1;
transform: translateY(0);
}
API ããŒã¹ã®ãªã¢ã«ã¿ã€ã æ€èšŒ
ã¯ã©ã€ã¢ã³ãåŽæ€èšŒã¯ãã©ãŒããããšã©ãŒãæ€åºããŸãããAPI ããŒã¹ã®æ€èšŒã¯ãé ä¿¡å¯èœæ§ã®æ€èšŒãäœ¿ãæšãŠã¡ãŒã«ã®æ€åºããªã¹ã¯ã¹ã³ã¢ãªã³ã°ãå«ãå æ¬çãªã¡ãŒã«ãã§ãã¯ãæäŸããŸãã
ãããŠã³ã¹ããã API ã³ãŒã«ã®å®è£
ãã¹ãŠã®ããŒã¹ãããŒã¯ã§ API ã³ãŒã«ãè¡ãããšã¯ããªãœãŒã¹ãç¡é§ã«ãããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãæªåãããŸãããŠãŒã¶ãŒãå ¥åãäžæåæ¢ãããŸã§åŸ ã€ãããŠã³ã¹ãå®è£ ããŸãïŒ
class RealTimeEmailVerifier {
constructor(options = {}) {
this.apiKey = options.apiKey;
this.apiUrl = options.apiUrl || 'https://api.billionverify.com/v1/verify';
this.debounceMs = options.debounceMs || 500;
this.minLength = options.minLength || 5;
this.debounceTimer = null;
this.cache = new Map();
}
async verify(email, callbacks = {}) {
const { onStart, onSuccess, onError, onComplete } = callbacks;
// Clear pending verification
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
// Skip if email is too short or invalid format
if (!this.shouldVerify(email)) {
return;
}
// Check cache first
if (this.cache.has(email)) {
const cachedResult = this.cache.get(email);
onSuccess?.(cachedResult);
onComplete?.();
return cachedResult;
}
// Debounce the API call
return new Promise((resolve) => {
this.debounceTimer = setTimeout(async () => {
onStart?.();
try {
const result = await this.callApi(email);
this.cache.set(email, result);
onSuccess?.(result);
resolve(result);
} catch (error) {
onError?.(error);
resolve(null);
} finally {
onComplete?.();
}
}, this.debounceMs);
});
}
shouldVerify(email) {
if (email.length < this.minLength) return false;
if (!email.includes('@')) return false;
const basicPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return basicPattern.test(email);
}
async callApi(email) {
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
if (!response.ok) {
throw new Error(`Verification failed: ${response.status}`);
}
return response.json();
}
clearCache() {
this.cache.clear();
}
}
ãã©ãŒã èŠçŽ ãšã®çµ±å
å æ¬ç㪠UI ãã£ãŒãããã¯ã䜿çšããŠãæ€èšŒããŒã«ããã©ãŒã ã«æ¥ç¶ããŸãïŒ
class EmailFormField {
constructor(inputSelector, options = {}) {
this.input = document.querySelector(inputSelector);
this.container = this.input.closest('.form-group');
this.feedback = this.container.querySelector('.feedback');
this.spinner = this.container.querySelector('.spinner');
this.verifier = new RealTimeEmailVerifier({
apiKey: options.apiKey,
debounceMs: 600
});
this.lastVerifiedEmail = null;
this.lastResult = null;
this.setupEventListeners();
}
setupEventListeners() {
this.input.addEventListener('input', (e) => {
this.handleInput(e.target.value);
});
this.input.addEventListener('blur', () => {
this.handleBlur();
});
}
handleInput(email) {
// Reset state while typing
this.setStatus('typing');
// Perform real-time verification
this.verifier.verify(email, {
onStart: () => this.setStatus('verifying'),
onSuccess: (result) => this.handleResult(email, result),
onError: (error) => this.handleError(error)
});
}
handleBlur() {
const email = this.input.value.trim();
if (!email) {
this.setStatus('empty');
return;
}
// If we haven't verified this email yet, do it now
if (email !== this.lastVerifiedEmail) {
this.verifier.verify(email, {
onStart: () => this.setStatus('verifying'),
onSuccess: (result) => this.handleResult(email, result),
onError: (error) => this.handleError(error)
});
}
}
handleResult(email, result) {
this.lastVerifiedEmail = email;
this.lastResult = result;
if (result.is_deliverable) {
this.setStatus('valid', 'Email address verified');
} else if (result.is_disposable) {
this.setStatus('warning', 'Please use a permanent email address');
} else if (!result.is_valid) {
this.setStatus('invalid', 'This email address appears to be invalid');
} else {
this.setStatus('warning', 'We could not verify this email address');
}
}
handleError(error) {
console.error('Verification error:', error);
// Don't block user on API errors
this.setStatus('neutral', '');
}
setStatus(status, message = '') {
const statusClasses = ['typing', 'verifying', 'valid', 'invalid', 'warning', 'empty', 'neutral'];
this.container.classList.remove(...statusClasses);
this.container.classList.add(status);
this.feedback.textContent = message;
this.spinner.style.display = status === 'verifying' ? 'block' : 'none';
}
isValid() {
return this.lastResult?.is_deliverable === true;
}
getResult() {
return this.lastResult;
}
}
ãªã¢ã«ã¿ã€ã æ€èšŒã®ããã® HTML æ§é
<div class="form-group">
<label for="email">Email Address</label>
<div class="input-wrapper">
<input
type="email"
id="email"
name="email"
autocomplete="email"
placeholder="you@example.com"
>
<div class="spinner" style="display: none;">
<svg class="animate-spin" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none" opacity="0.25"/>
<path fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
</div>
<div class="status-icon"></div>
</div>
<div class="feedback"></div>
</div>
ãšããžã±ãŒã¹ãšãšã©ãŒã®åŠç
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ãè¯å¥œãªãŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãç¶æããããã«ãããŸããŸãªãšããžã±ãŒã¹ãé©åã«åŠçããå¿ èŠããããŸãã
ãããã¯ãŒã¯é害
ãããã¯ãŒã¯ã®åé¡ã«ãã API ã³ãŒã«ã倱æããå Žåããã©ãŒã éä¿¡ãå®å šã«ãããã¯ããªãã§ãã ããïŒ
class ResilientEmailVerifier extends RealTimeEmailVerifier {
constructor(options) {
super(options);
this.maxRetries = options.maxRetries || 2;
this.retryDelay = options.retryDelay || 1000;
}
async callApi(email, attempt = 1) {
try {
return await super.callApi(email);
} catch (error) {
if (attempt < this.maxRetries) {
await this.delay(this.retryDelay * attempt);
return this.callApi(email, attempt + 1);
}
// Return a neutral result on failure
return {
email,
is_valid: true,
is_deliverable: null,
verification_status: 'unknown',
error: 'Verification unavailable'
};
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
ã¬ãŒãå¶é
API ã¯ã©ãŒã¿å ã«çãŸãããã«ãã€ã³ããªãžã§ã³ããªã¬ãŒãå¶éãå®è£ ããŸãïŒ
class RateLimitedVerifier {
constructor(options) {
this.verifier = new RealTimeEmailVerifier(options);
this.requestQueue = [];
this.requestsPerMinute = options.requestsPerMinute || 60;
this.requestTimestamps = [];
}
async verify(email, callbacks) {
// Clean old timestamps
const oneMinuteAgo = Date.now() - 60000;
this.requestTimestamps = this.requestTimestamps.filter(t => t > oneMinuteAgo);
// Check if we're at the limit
if (this.requestTimestamps.length >= this.requestsPerMinute) {
const oldestRequest = this.requestTimestamps[0];
const waitTime = oldestRequest + 60000 - Date.now();
if (waitTime > 0) {
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
this.requestTimestamps.push(Date.now());
return this.verifier.verify(email, callbacks);
}
}
äœéæ¥ç¶ã®åŠç
äœéæ¥ç¶ã®ãŠãŒã¶ãŒã«ãã£ãŒãããã¯ãæäŸããŸãïŒ
class TimeoutAwareVerifier {
constructor(options) {
this.verifier = new RealTimeEmailVerifier(options);
this.timeout = options.timeout || 10000;
}
async verify(email, callbacks) {
const { onStart, onSuccess, onError, onComplete, onTimeout } = callbacks;
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Verification timeout')), this.timeout);
});
onStart?.();
try {
const result = await Promise.race([
this.verifier.verify(email, {}),
timeoutPromise
]);
onSuccess?.(result);
return result;
} catch (error) {
if (error.message === 'Verification timeout') {
onTimeout?.();
} else {
onError?.(error);
}
} finally {
onComplete?.();
}
}
}
ãªã¢ã«ã¿ã€ã æ€èšŒã® UX ãã¹ããã©ã¯ãã£ã¹
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã®å®è£ ã«ã¯ããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãžã®çްå¿ã®æ³šæãå¿ èŠã§ããå®è£ ãäžååã ãšããŠãŒã¶ãŒãã€ã©ã€ã©ããããã©ãŒã ã®é¢è±ãå¢å ãããå¯èœæ§ããããŸãã
ã¿ã€ãã³ã°ãšãã£ãŒãããã¯
ãã¹ãŠã®ããŒã¹ãããŒã¯ã§æ€èšŒããªãïŒããã¯é床㮠API ã³ãŒã«ãšæ°ãæ£ã UI ã®å€æŽãåŒãèµ·ãããŸãã400ã600ms ã®é å»¶ã§ãããŠã³ã¹ã䜿çšããŸãã
ããŒãã£ã³ã°ç¶æ ãæç¢ºã«è¡šç€ºïŒãŠãŒã¶ãŒã¯æ€èšŒããã€è¡ãããŠããããçè§£ããå¿ èŠããããŸããæ§ãããªã¹ãããŒãŸãã¯èåã¢ãã¡ãŒã·ã§ã³ã¯ãæ°ãæ£ãããšãªãã¢ã¯ãã£ããã£ã瀺ããŸãã
å³åº§ã®æ§æãã£ãŒãããã¯ãæäŸïŒåºæ¬çãªãã©ãŒãããæ€èšŒã¯ãAPI ã³ãŒã«ãªãã§å³åº§ã«è¡ãããšãã§ããŸããã¡ãŒã«ãå®å šã«èŠãããšãã« API æ€èšŒãä¿åããŸãã
ãšã©ãŒã¡ãã»ãŒãžã®ã¬ã€ãã©ã€ã³
å ·äœçã§åœ¹ç«ã€å 容ã«ïŒãç¡å¹ãªã¡ãŒã«ãã®ä»£ããã«ãããã®ã¡ãŒã«ãã¡ã€ã³ã¯ååšããªãããã§ããgmail.com ã®ããšã§ãããïŒããšèšããŸãã
å¯èœãªå Žåã¯ææ¡ãæäŸïŒãã¡ã€ã³ãã¿ã€ããã¹ã®ããã«èŠããå Žåã¯ãä¿®æ£ãææ¡ããŸãããgmial.comãã®ãããªäžè¬çãªã¿ã€ããã¹ã¯ããgmail.com ã®ããšã§ãããïŒããšä¿ãå¿ èŠããããŸãã
æ»æçã«ãªããªãïŒäœ¿ãæšãŠã¡ãŒã«ã«é¢ããèŠåã¯ãå±è²¬ããã®ã§ã¯ãªããæ å ±ãæäŸããå¿ èŠããããŸãããã¢ã«ãŠã³ãã®ã»ãã¥ãªãã£ã®ãããæ°žç¶çãªã¡ãŒã«ã¢ãã¬ã¹ã䜿çšããŠãã ãããã®æ¹ããäœ¿ãæšãŠã¡ãŒã«ã¯èš±å¯ãããŠããŸããããããè¯ãã§ãã
ããã°ã¬ãã·ããšã³ãã³ã¹ã¡ã³ã
æ€èšŒãèŠä»¶ã§ã¯ãªããæ¡åŒµæ©èœãšããŠå®è£ ããŸãïŒ
class ProgressiveEmailVerification {
constructor(inputSelector, options) {
this.input = document.querySelector(inputSelector);
this.form = this.input.closest('form');
this.hasApiAccess = !!options.apiKey;
// Always enable basic validation
this.enableBasicValidation();
// Enable API verification if available
if (this.hasApiAccess) {
this.enableApiVerification(options);
}
}
enableBasicValidation() {
this.input.addEventListener('blur', () => {
const email = this.input.value.trim();
if (email && !this.isValidFormat(email)) {
this.showError('Please enter a valid email address');
}
});
}
enableApiVerification(options) {
this.verifier = new RealTimeEmailVerifier(options);
this.input.addEventListener('input', (e) => {
this.verifier.verify(e.target.value, {
onSuccess: (result) => this.handleVerificationResult(result)
});
});
}
isValidFormat(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
handleVerificationResult(result) {
// Enhanced verification results
}
showError(message) {
// Error display logic
}
}
ãã¬ãŒã ã¯ãŒã¯åºæã®å®è£
ææ°ã® JavaScript ãã¬ãŒã ã¯ãŒã¯ã¯ããªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã广çã«å®è£ ããããã®ãã¿ãŒã³ãæäŸããŸãã
React å®è£
import { useState, useCallback, useEffect, useRef } from 'react';
function useEmailVerification(apiKey, options = {}) {
const [status, setStatus] = useState('idle');
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const debounceRef = useRef(null);
const cacheRef = useRef(new Map());
const verify = useCallback(async (email) => {
// Clear pending verification
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
// Skip invalid emails
if (!email || !email.includes('@') || email.length < 5) {
setStatus('idle');
return;
}
// Check cache
if (cacheRef.current.has(email)) {
setResult(cacheRef.current.get(email));
setStatus('success');
return;
}
// Debounce API call
debounceRef.current = setTimeout(async () => {
setStatus('loading');
try {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
if (!response.ok) throw new Error('Verification failed');
const data = await response.json();
cacheRef.current.set(email, data);
setResult(data);
setStatus('success');
} catch (err) {
setError(err);
setStatus('error');
}
}, options.debounceMs || 500);
}, [apiKey, options.debounceMs]);
return { verify, status, result, error };
}
function EmailInput({ apiKey }) {
const [email, setEmail] = useState('');
const { verify, status, result } = useEmailVerification(apiKey);
useEffect(() => {
verify(email);
}, [email, verify]);
const getStatusClass = () => {
if (status === 'loading') return 'verifying';
if (status === 'success' && result?.is_deliverable) return 'valid';
if (status === 'success' && !result?.is_deliverable) return 'invalid';
return '';
};
return (
<div className={`form-group ${getStatusClass()}`}>
<label htmlFor="email">Email Address</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
/>
{status === 'loading' && <span className="spinner" />}
{status === 'success' && result && (
<span className="feedback">
{result.is_deliverable
? 'â Email verified'
: 'This email may not be deliverable'}
</span>
)}
</div>
);
}
Vue.js å®è£
<template>
<div :class="['form-group', statusClass]">
<label for="email">Email Address</label>
<div class="input-wrapper">
<input
type="email"
id="email"
v-model="email"
@input="handleInput"
placeholder="you@example.com"
/>
<span v-if="isVerifying" class="spinner"></span>
</div>
<span v-if="feedbackMessage" class="feedback">
{{ feedbackMessage }}
</span>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue';
import { useDebounceFn } from '@vueuse/core';
export default {
props: {
apiKey: { type: String, required: true }
},
setup(props) {
const email = ref('');
const status = ref('idle');
const result = ref(null);
const cache = new Map();
const verifyEmail = useDebounceFn(async (emailValue) => {
if (!emailValue || !emailValue.includes('@')) {
status.value = 'idle';
return;
}
if (cache.has(emailValue)) {
result.value = cache.get(emailValue);
status.value = 'success';
return;
}
status.value = 'loading';
try {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${props.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: emailValue })
});
const data = await response.json();
cache.set(emailValue, data);
result.value = data;
status.value = 'success';
} catch (error) {
status.value = 'error';
}
}, 500);
const handleInput = () => {
verifyEmail(email.value);
};
const isVerifying = computed(() => status.value === 'loading');
const statusClass = computed(() => {
if (status.value === 'loading') return 'verifying';
if (status.value === 'success' && result.value?.is_deliverable) return 'valid';
if (status.value === 'success' && !result.value?.is_deliverable) return 'invalid';
return '';
});
const feedbackMessage = computed(() => {
if (status.value !== 'success' || !result.value) return '';
return result.value.is_deliverable
? 'â Email verified'
: 'This email may not be deliverable';
});
return {
email,
handleInput,
isVerifying,
statusClass,
feedbackMessage
};
}
};
</script>
ããã©ãŒãã³ã¹æé©åæŠç¥
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ãæ éã«å®è£ ããªããšããŒãžã®ããã©ãŒãã³ã¹ã«åœ±é¿ãäžããå¯èœæ§ããããŸããã¹ã ãŒãºãªãŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãç¶æããããã«ããããã®æé©åæŠç¥ãé©çšããŸãã
æ€èšŒçµæã®ãã£ãã·ã³ã°
åé·ãª API ã³ãŒã«ãåé¿ããããã«ãã¯ã©ã€ã¢ã³ãåŽãã£ãã·ã¥ãå®è£ ããŸãïŒ
class VerificationCache {
constructor(options = {}) {
this.maxSize = options.maxSize || 100;
this.ttl = options.ttl || 300000; // 5 minutes
this.cache = new Map();
}
get(email) {
const normalized = email.toLowerCase().trim();
const entry = this.cache.get(normalized);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.cache.delete(normalized);
return null;
}
return entry.result;
}
set(email, result) {
const normalized = email.toLowerCase().trim();
// Enforce max size with LRU eviction
if (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(normalized, {
result,
expiresAt: Date.now() + this.ttl
});
}
clear() {
this.cache.clear();
}
}
æ€èšŒã¢ãžã¥ãŒã«ã®é å»¶èªã¿èŸŒã¿
å¿ èŠãªãšãã«ã®ã¿æ€èšŒã¢ãžã¥ãŒã«ãèªã¿èŸŒã¿ãŸãïŒ
async function initEmailVerification(inputSelector, options) {
// Only load when user focuses on email field
const input = document.querySelector(inputSelector);
input.addEventListener('focus', async function onFocus() {
input.removeEventListener('focus', onFocus);
const { RealTimeEmailVerifier } = await import('./email-verifier.js');
const verifier = new RealTimeEmailVerifier(options);
input.addEventListener('input', (e) => {
verifier.verify(e.target.value, {
onSuccess: (result) => updateUI(result),
onError: (error) => handleError(error)
});
});
}, { once: true });
}
ãã³ãã«ãµã€ãºã®åæž
ããªãŒã·ã§ã€ãã³ã°ãšã³ãŒãåå²ã䜿çšããŠãããŒãžèªã¿èŸŒã¿ãžã®åœ±é¿ãæå°éã«æããŸãïŒ
// email-verifier/index.js - Main entry point
export { RealTimeEmailVerifier } from './verifier';
export { EmailFormField } from './form-field';
// email-verifier/lite.js - Lightweight version for basic validation
export { BasicEmailValidator } from './basic-validator';
æ€èšŒã®å¹æã®æž¬å®
ãã©ãŒã ã«ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒãã©ã®ããã«åœ±é¿ããããçè§£ããããã«ãäž»èŠãªã¡ããªã¯ã¹ã远跡ããŸãã
äž»èŠæ¥çžŸè©äŸ¡ææš
æ€èšŒæåçïŒæ€èšŒã«åæ Œããã¡ãŒã«ã®å²åãäœãçã¯ãUX ã®åé¡ãŸãã¯ã¿ãŒã²ãã£ã³ã°ã®åé¡ã瀺ããŠããå¯èœæ§ããããŸãã
ãã©ãŒã å®äºçïŒæ€èšŒãå®è£ ããååŸã®å®äºçãæ¯èŒããŸããè¯å¥œãªå®è£ ã¯ãå®äºçãç¶æãŸãã¯æ¹åããå¿ èŠããããŸãã
ç¡å¹ãªã¡ãŒã«çïŒãã©ãŒã å ¥åäžã«æ€åºããã³ä¿®æ£ãããç¡å¹ãªã¡ãŒã«ã®æ°ãšãåŸã§çºèŠãããç¡å¹ãªã¡ãŒã«ã®æ°ã远跡ããŸãã
API å¿çæéïŒæ€èšŒé床ãç£èŠããŸããé ãå¿çã¯ããŠãŒã¶ãŒãã€ã©ã€ã©ãããé¢è±ãå¢å ãããŸãã
åæå®è£
class VerificationAnalytics {
constructor(analyticsProvider) {
this.analytics = analyticsProvider;
}
trackVerificationStart(email) {
this.analytics.track('email_verification_started', {
domain: this.extractDomain(email),
timestamp: Date.now()
});
}
trackVerificationComplete(email, result, duration) {
this.analytics.track('email_verification_completed', {
domain: this.extractDomain(email),
is_valid: result.is_valid,
is_deliverable: result.is_deliverable,
is_disposable: result.is_disposable,
risk_score: result.risk_score,
duration_ms: duration
});
}
trackVerificationError(email, error) {
this.analytics.track('email_verification_error', {
domain: this.extractDomain(email),
error_type: error.name,
error_message: error.message
});
}
trackFormSubmission(email, verificationResult) {
this.analytics.track('form_submitted_with_verification', {
email_verified: !!verificationResult,
verification_passed: verificationResult?.is_deliverable,
verification_status: verificationResult?.verification_status
});
}
extractDomain(email) {
return email.split('@')[1]?.toLowerCase() || 'unknown';
}
}
ã»ãã¥ãªãã£ã«é¢ããèæ ®äºé
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã«ã¯ããŠãŒã¶ãŒããŒã¿ãå€éšãµãŒãã¹ã«éä¿¡ããããšãå«ãŸããŸãããŠãŒã¶ãŒã®ãã©ã€ãã·ãŒãä¿è·ããããã«ãé©åãªã»ãã¥ãªãã£å¯Ÿçãå®è£ ããŸãã
API ããŒã®ä¿è·
ã¯ã©ã€ã¢ã³ãåŽã®ã³ãŒãã§ API ããŒãå ¬éããªãã§ãã ãããããã¯ãšã³ããããã·ã䜿çšããŸãïŒ
// Backend proxy endpoint (Node.js/Express)
app.post('/api/verify-email', async (req, res) => {
const { email } = req.body;
// Validate input
if (!email || typeof email !== 'string') {
return res.status(400).json({ error: 'Invalid email' });
}
// Rate limiting per IP
const clientIp = req.ip;
if (await isRateLimited(clientIp)) {
return res.status(429).json({ error: 'Too many requests' });
}
try {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const result = await response.json();
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Verification service unavailable' });
}
});
å ¥åã®ãµãã¿ã€ãº
åŠçããåã«ãåžžã«ã¡ãŒã«å ¥åããµãã¿ã€ãºããŸãïŒ
function sanitizeEmail(email) {
if (typeof email !== 'string') return '';
return email
.toLowerCase()
.trim()
.replace(/[<>\"']/g, '') // Remove potential XSS characters
.substring(0, 254); // Max email length per RFC
}
ãŸãšã
ãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã¯ããã©ãŒã ã®ã€ã³ã¿ã©ã¯ã·ã§ã³ããã€ã©ã€ã©ããåœãŠæšéã²ãŒã ãããèªä¿¡ã«æºã¡ãæ¡å ããããšã¯ã¹ããªãšã³ã¹ã«å€ããŸãããŠãŒã¶ãŒãå ¥åããéã«ã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒããããšã§ãã·ã¹ãã ã«ç¡å¹ãªããŒã¿ãå ¥ãããšãé²ããªããããŠãŒã¶ãŒãæåããã®ã«åœ¹ç«ã€å³åº§ã®ãã£ãŒãããã¯ãæäŸããŸãã
æåããå®è£ ã®äž»èŠãªååã«ã¯ã次ã®ãã®ããããŸãïŒ
æ€èšŒãéå±€åïŒå³åº§ã®ã¯ã©ã€ã¢ã³ãåŽãã©ãŒããããã§ãã¯ãšå æ¬ç㪠API æ€èšŒãçµã¿åãããŸããåå±€ã¯ç°ãªãã¿ã€ãã®åé¡ãæ€åºããŸãã
ãŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ã®ããã«æé©åïŒãããŠã³ã¹ã䜿çšããŠé床㮠API ã³ãŒã«ãé²ããæç¢ºãªèŠèŠçãã£ãŒãããã¯ãæäŸããæ€èšŒãµãŒãã¹ã®åé¡ã«ãã£ãŠãŠãŒã¶ãŒããããã¯ããªãã§ãã ããã
é害ãåªé ã«åŠçïŒãããã¯ãŒã¯ãšã©ãŒãš API ã¿ã€ã ã¢ãŠãã¯ããã©ãŒã éä¿¡ã劚ããã¹ãã§ã¯ãããŸãããé«åºŠãªæ€èšŒãå©çšã§ããªãå Žåã¯ãåºæ¬çãªæ€èšŒã«ãã©ãŒã«ããã¯ããŸãã
ç£èŠãšååŸ©ïŒæ€èšŒã¡ããªã¯ã¹ã远跡ããŠãå®è£ ããã©ãŒã ã®å®äºãšããŒã¿å質ã«ã©ã®ããã«åœ±é¿ããããçè§£ããŸãããã®ããŒã¿ã䜿çšããŠã¢ãããŒããæ¹åããŸãã
ãŠãŒã¶ãŒããŒã¿ãä¿è·ïŒAPI ããŒãä¿è·ããããã«ããã¯ãšã³ããããã·ãä»ããŠæ€èšŒãªã¯ãšã¹ããã«ãŒãã£ã³ã°ããã¬ãŒãå¶éãå®è£ ãããã¹ãŠã®å ¥åããµãã¿ã€ãºããŸãã
BillionVerify ã®ã¡ãŒã«æ€èšŒ API ã¯ãé ä¿¡å¯èœæ§ãã§ãã¯ãäœ¿ãæšãŠã¡ãŒã«æ€åºããªã¹ã¯ã¹ã³ã¢ãªã³ã°ãå«ãå æ¬çãªãªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒã®ããã®ã€ã³ãã©ã¹ãã©ã¯ãã£ãæäŸããŸãããã®ã¬ã€ãã®å®è£ ãã¿ãŒã³ãšçµã¿åãããããšã§ãåªãããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãç¶æããªããé«å質ã®ã¡ãŒã«ã¢ãã¬ã¹ããã£ããã£ãããã©ãŒã ãšã¯ã¹ããªãšã³ã¹ãæ§ç¯ã§ããŸãã
åºæ¬çãªã¯ã©ã€ã¢ã³ãåŽæ€èšŒããå§ããŠãç¹å®ã®ããŒãºã«åºã¥ã㊠API ããŒã¹ã®æ€èšŒã§æ®µéçã«åŒ·åããŸãããªã¢ã«ã¿ã€ã ã¡ãŒã«æ€èšŒãžã®æè³ã¯ãããŠã³ã¹çã®åæžãã¡ãŒã«é ä¿¡å¯èœæ§ã®åäžãé«å質ã®ãŠãŒã¶ãŒããŒã¿ãéããŠå©çããããããŸãã