Vue.js
Email checker for Vue.js. Real-time email verification in Vue components and forms.
Vue 3 アプリケーションに EmailVerify を統合して、リアルタイムメール検証を実現します。このガイドでは、基本的なセットアップから高度なパターンまで、Composition API と Options API の両方の実装をカバーします。
インストール
EmailVerify パッケージをインストールし、プロジェクトを設定します。
npm install emailverifyyarn add emailverifypnpm add emailverifyクイックスタート
Composition API
Composition API を使用してコンポーネント内でリアクティブなメール検証を行います。
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="Enter email address"
@blur="verifyEmail"
class="email-input"
/>
<div v-if="isLoading" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
<span v-if="result.status === 'valid'">✓ Valid email</span>
<span v-else-if="result.status === 'invalid'">✗ Invalid email</span>
<span v-else>? Could not verify</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { emailverify } from 'emailverify';
const email = ref('');
const isLoading = ref(false);
const result = ref<any>(null);
const verifyEmail = async () => {
if (!email.value) return;
isLoading.value = true;
try {
result.value = await emailverify.verify({
email: email.value,
apiKey: import.meta.env.VITE_EMAILVERIFY_API_KEY
});
} catch (error) {
console.error('Verification failed:', error);
result.value = null;
} finally {
isLoading.value = false;
}
};
</script>
<style scoped>
.email-input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
width: 100%;
}
.status {
margin-top: 8px;
padding: 8px 12px;
border-radius: 4px;
font-size: 14px;
}
.status.valid {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.invalid {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status.loading {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
</style>Options API
Options API パターンを使用するプロジェクト向け:
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="Enter email address"
@blur="verifyEmail"
class="email-input"
/>
<div v-if="isLoading" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
{{ statusMessage }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { emailverify } from 'emailverify';
export default defineComponent({
name: 'EmailVerification',
data() {
return {
email: '',
isLoading: false,
result: null
};
},
computed: {
statusMessage(): string {
if (this.result?.status === 'valid') return '✓ Valid email';
if (this.result?.status === 'invalid') return '✗ Invalid email';
return '? Could not verify';
}
},
methods: {
async verifyEmail() {
if (!this.email) return;
this.isLoading = true;
try {
this.result = await emailverify.verify({
email: this.email,
apiKey: import.meta.env.VITE_EMAILVERIFY_API_KEY
});
} catch (error) {
console.error('Verification failed:', error);
this.result = null;
} finally {
this.isLoading = false;
}
}
}
});
</script>コンポーザブルリファレンス
useEmailVerification
メール検証の状態とロジックを管理するためのカスタムコンポーザブル。
import { ref, computed } from 'vue';
import { emailverify } from 'emailverify';
export function useEmailVerification(apiKey: string) {
const email = ref('');
const isLoading = ref(false);
const error = ref<string | null>(null);
const result = ref<any>(null);
const isValid = computed(() => result.value?.status === 'valid');
const isInvalid = computed(() => result.value?.status === 'invalid');
const isDisposable = computed(() => result.value?.result?.disposable || false);
const verify = async (emailToVerify?: string) => {
const emailAddress = emailToVerify || email.value;
if (!emailAddress) {
error.value = 'Email address is required';
return;
}
isLoading.value = true;
error.value = null;
try {
result.value = await emailverify.verify({
email: emailAddress,
apiKey
});
} catch (err) {
error.value = err instanceof Error ? err.message : 'Verification failed';
result.value = null;
} finally {
isLoading.value = false;
}
};
const reset = () => {
email.value = '';
result.value = null;
error.value = null;
isLoading.value = false;
};
return {
email,
isLoading,
error,
result,
isValid,
isInvalid,
isDisposable,
verify,
reset
};
}useEmailInput
デバウンスとキャッシュを備えたメール入力処理用コンポーザブル。
import { ref, computed, watch } from 'vue';
import { useEmailVerification } from './useEmailVerification';
const CACHE_DURATION = 30 * 60 * 1000; // 30 minutes
export function useEmailInput(apiKey: string) {
const email = ref('');
const cache = new Map<string, { result: any; timestamp: number }>();
const { verify: verifyWithAPI, isLoading, error, result } = useEmailVerification(apiKey);
const verify = async () => {
if (!email.value) return;
// Check cache first
const cached = cache.get(email.value);
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
result.value = cached.result;
return;
}
// Verify with API
await verifyWithAPI(email.value);
// Cache the result
if (result.value) {
cache.set(email.value, {
result: result.value,
timestamp: Date.now()
});
}
};
const clearCache = () => cache.clear();
return {
email,
isLoading,
error,
result,
verify,
clearCache
};
}コンポーネント統合
VeeValidate 統合
VeeValidate とメール検証を統合してフォームバリデーションを行います。
<template>
<Form @submit="onSubmit" class="form">
<div class="form-group">
<label>Email Address</label>
<Field
name="email"
type="email"
placeholder="your@email.com"
v-slot="{ field, meta }"
:rules="validateEmail"
>
<input
v-bind="field"
:class="{ 'input-error': meta.touched && !meta.valid }"
@blur="verifyEmail(field.value)"
/>
</Field>
<div v-if="verificationResult" :class="['verification-status', verificationResult.status]">
{{ getStatusMessage(verificationResult) }}
</div>
<ErrorMessage name="email" class="error-message" />
</div>
<button type="submit" :disabled="isLoading">
{{ isLoading ? 'Verifying...' : 'Submit' }}
</button>
</Form>
</template>
<script setup lang="ts">
import { Form, Field, ErrorMessage } from 'vee-validate';
import * as yup from 'yup';
import { ref } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
const schema = yup.object({
email: yup.string().email('Invalid email').required('Email is required')
});
const { verify, result: verificationResult, isLoading } = useEmailVerification(
import.meta.env.VITE_EMAILVERIFY_API_KEY
);
const validateEmail = async (value: string) => {
if (!value) return 'Email is required';
try {
await schema.validate({ email: value });
return true;
} catch {
return 'Invalid email format';
}
};
const verifyEmail = async (email: string) => {
if (email) {
await verify(email);
}
};
const getStatusMessage = (result: any) => {
switch (result.status) {
case 'valid':
return '✓ Valid email address';
case 'invalid':
return '✗ Invalid email address';
case 'unknown':
return '? Could not verify this email';
default:
return 'Unknown status';
}
};
const onSubmit = (values: any) => {
if (verificationResult.value?.status === 'valid') {
console.log('Form submitted:', values);
}
};
</script>
<style scoped>
.form-group {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
input.input-error {
border-color: #dc3545;
background-color: #fff5f5;
}
.error-message {
color: #dc3545;
font-size: 12px;
margin-top: 4px;
}
.verification-status {
margin-top: 8px;
padding: 8px 12px;
border-radius: 4px;
font-size: 13px;
}
.verification-status.valid {
background-color: #d4edda;
color: #155724;
}
.verification-status.invalid {
background-color: #f8d7da;
color: #721c24;
}
</style>Element Plus 統合
EmailVerify を Element Plus フォームコンポーネントで使用します。
<template>
<el-form :model="form" @submit.prevent="handleSubmit">
<el-form-item label="Email" prop="email">
<el-input
v-model="form.email"
type="email"
placeholder="Enter email"
@blur="verifyEmail"
:loading="isLoading"
/>
<el-alert
v-if="verificationResult"
:title="getStatusTitle()"
:type="getAlertType()"
:closable="false"
style="margin-top: 8px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleSubmit"
:loading="isLoading"
>
Submit
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
const form = reactive({
email: ''
});
const { verify, result: verificationResult, isLoading } = useEmailVerification(
import.meta.env.VITE_EMAILVERIFY_API_KEY
);
const verifyEmail = async () => {
if (form.email) {
await verify(form.email);
}
};
const getStatusTitle = () => {
switch (verificationResult.value?.status) {
case 'valid':
return 'Email is valid';
case 'invalid':
return 'Email is invalid';
default:
return 'Could not verify email';
}
};
const getAlertType = () => {
return verificationResult.value?.status === 'valid' ? 'success' : 'error';
};
const handleSubmit = async () => {
await verifyEmail();
if (verificationResult.value?.status === 'valid') {
console.log('Email verified, submitting form...');
}
};
</script>Nuxt 3 での SSR
サーバーサイド検証
セキュリティとパフォーマンス向上のため、サーバーでメールを検証します。
// server/api/verify-email.ts
import { emailverify } from 'emailverify';
export default defineEventHandler(async (event) => {
const { email } = await readBody(event);
if (!email) {
throw createError({
statusCode: 400,
statusMessage: 'Email is required'
});
}
try {
const result = await emailverify.verify({
email,
apiKey: process.env.EMAILVERIFY_API_KEY!
});
return result;
} catch (error) {
throw createError({
statusCode: 500,
statusMessage: 'Verification failed'
});
}
});Nuxt でのクライアントサイド統合
Nuxt コンポーネントからサーバーエンドポイントを使用します。
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="your@email.com"
@blur="verifyEmail"
/>
<div v-if="pending" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
{{ statusMessage }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
const email = ref('');
const { data: result, pending, execute: verifyEmail } = await useFetch(
'/api/verify-email',
{
method: 'POST',
body: computed(() => ({ email: email.value })),
immediate: false
}
);
const statusMessage = computed(() => {
if (!result.value) return '';
switch (result.value.status) {
case 'valid':
return '✓ Valid email';
case 'invalid':
return '✗ Invalid email';
default:
return '? Unknown status';
}
});
</script>高度なパターン
デバウンス検証
API コールを減らすためのデバウンス付きメール検証。
import { ref, watch } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
export function useDebouncedEmailVerification(apiKey: string, delay = 500) {
const email = ref('');
const debounceTimer = ref<NodeJS.Timeout | null>(null);
const { verify, ...rest } = useEmailVerification(apiKey);
watch(email, (newEmail) => {
if (debounceTimer.value) {
clearTimeout(debounceTimer.value);
}
debounceTimer.value = setTimeout(() => {
if (newEmail) {
verify(newEmail);
}
}, delay);
});
return { email, verify, ...rest };
}メールキャッシュ戦略
API コールを減らすためのインテリジェントキャッシュを実装。
const CACHE_KEY_PREFIX = 'emailverify_';
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
export function useCachedEmailVerification(apiKey: string) {
const { verify: verifyWithAPI, result, isLoading } = useEmailVerification(apiKey);
const getCacheKey = (email: string) => `${CACHE_KEY_PREFIX}${email}`;
const verify = async (email: string) => {
const cacheKey = getCacheKey(email);
const cached = localStorage.getItem(cacheKey);
if (cached) {
const { result: cachedResult, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp < CACHE_DURATION) {
result.value = cachedResult;
return;
}
}
await verifyWithAPI(email);
if (result.value) {
localStorage.setItem(
cacheKey,
JSON.stringify({
result: result.value,
timestamp: Date.now()
})
);
}
};
const clearCache = (email?: string) => {
if (email) {
localStorage.removeItem(getCacheKey(email));
} else {
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith(CACHE_KEY_PREFIX)) {
localStorage.removeItem(key);
}
});
}
};
return { verify, result, isLoading, clearCache };
}エラー処理とリトライ
リトライロジックを備えた堅牢なエラー処理を実装。
export function useEmailVerificationWithRetry(apiKey: string, maxRetries = 3) {
const { verify: verifyWithAPI, result, isLoading, error } = useEmailVerification(apiKey);
const verifyWithRetry = async (email: string, retryCount = 0): Promise<any> => {
try {
await verifyWithAPI(email);
return result.value;
} catch (err) {
if (retryCount < maxRetries) {
// Wait before retry (exponential backoff)
await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
return verifyWithRetry(email, retryCount + 1);
}
throw err;
}
};
return { verifyWithRetry, result, isLoading, error };
}TypeScript サポート
EmailVerify SDK には型定義を含む完全な TypeScript サポートが含まれています。
interface VerificationRequest {
email: string;
apiKey: string;
}
interface VerificationResult {
status: 'valid' | 'invalid' | 'unknown' | 'accept_all';
result: {
disposable: boolean;
smtp_valid: boolean;
format_valid: boolean;
email_provider: string;
risk_level: 'low' | 'medium' | 'high';
};
score: number;
}
interface UseEmailVerificationReturn {
email: Ref<string>;
isLoading: Ref<boolean>;
error: Ref<string | null>;
result: Ref<VerificationResult | null>;
isValid: ComputedRef<boolean>;
isInvalid: ComputedRef<boolean>;
isDisposable: ComputedRef<boolean>;
verify: (emailToVerify?: string) => Promise<void>;
reset: () => void;
}テスト
Vitest によるユニットテスト
Vitest でメール検証コンポーザブルをテストします。
import { describe, it, expect, vi } from 'vitest';
import { useEmailVerification } from './composables/useEmailVerification';
vi.mock('emailverify', () => ({
emailverify: {
verify: vi.fn()
}
}));
describe('useEmailVerification', () => {
it('verifies valid email', async () => {
const { verify, result, isValid } = useEmailVerification('test-key');
await verify('user@example.com');
expect(result.value?.status).toBe('valid');
expect(isValid.value).toBe(true);
});
it('marks invalid email', async () => {
const { verify, result, isInvalid } = useEmailVerification('test-key');
await verify('invalid@');
expect(result.value?.status).toBe('invalid');
expect(isInvalid.value).toBe(true);
});
it('handles verification errors', async () => {
const { verify, error } = useEmailVerification('test-key');
try {
await verify('');
} catch (err) {
expect(error.value).toBeTruthy();
}
});
});コンポーネントテスト
Vue Test Utils で Vue コンポーネントをテストします。
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import EmailVerification from './EmailVerification.vue';
describe('EmailVerification', () => {
it('renders input field', () => {
const wrapper = mount(EmailVerification);
expect(wrapper.find('input[type="email"]').exists()).toBe(true);
});
it('shows verification status', async () => {
const wrapper = mount(EmailVerification);
await wrapper.find('input').setValue('user@example.com');
await wrapper.find('input').trigger('blur');
expect(wrapper.find('.status').exists()).toBe(true);
});
it('shows loading state during verification', async () => {
const wrapper = mount(EmailVerification);
await wrapper.find('input').setValue('user@example.com');
expect(wrapper.find('.status.loading').exists()).toBe(true);
});
});