#form (3)
前提
- Astro 5 で静的サイト生成(
output: 'static') - Cloudflare Pages にデプロイ
- フォーム送信後の webhook 処理が必要
実装パターン
1. Pages Functions の配置
functions/
contact.ts # POST /api/contact
form-utils.ts # 共通ロジック
2. Astro コンポーネント側
---
// src/components/ContactForm.astro
// フォーム validation / UX は astro:islands で React/Svelte
---
<form id="contact-form">
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button type="submit">送信</button>
</form>
<script>
document.getElementById('contact-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify(Object.fromEntries(formData))
});
// ...
});
</script>
3. Pages Functions で webhook 実行
// functions/contact.ts
export const onRequest: PagesFunction = async (context) => {
try {
const { name, email, message } = await context.request.json();
// Validation
if (!name || !email || !message) {
return new Response('Missing fields', { status: 400 });
}
// Webhook 実行
const slackResponse = await fetch(
context.env.SLACK_WEBHOOK_URL,
{
method: 'POST',
body: JSON.stringify({
text: `新規お問い合わせ\n名前: ${name}\nメール: ${email}\nメッセージ: ${message}`
})
}
);
if (!slackResponse.ok) {
throw new Error('Slack webhook failed');
}
return new Response(
JSON.stringify({ success: true }),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('Form submission error:', error);
return new Response('Internal server error', { status: 500 });
}
};
ポイント
- 環境変数:
wrangler.tomlでenvセクションに Slack Webhook URL を定義 - CORS: Astro 静的ファイルとエッジ関数は同一オリジン(CORS 不要)
- キャッシング: Pages Functions は自動的にキャッシュされない(毎回実行)
メリット
- サーバーレス・自動スケーリング
- Google Firebase/Cloud Run より簡潔
- Cloudflare Analytics Engine で リクエスト監視可能
概要
Next.js App Router + Firebase → Astro + CF Pages への移行時のフォーム実装パターン。reCAPTCHA v3、Firebase Cloud Functions、HubSpot 埋め込みをどう置き換えるか。
アーキテクチャ変更
| 要素 | Next.js | Astro |
|---|---|---|
| CAPTCHA | reCAPTCHA v3 | Cloudflare Turnstile |
| バックエンド | Firebase Cloud Functions | CF Pages Functions (/api/*) |
| フォーム送信 | Server Action + Firebase | CF API ルート |
| HubSpot | 埋め込み(SSR時にスキップ) | React Island化 |
実装フロー
1. Turnstile の統合
<!-- Astro コンポーネント -->
<div id="turnstile-container"></div>
<script>
import Turnstile from '@/components/Turnstile.tsx';
</script>
<Turnstile />
2. API ルートの実装
// src/pages/api/submit-form.ts
export async function POST({ request }) {
const formData = await request.json();
// Turnstile 検証
const verifyResponse = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body: JSON.stringify({
secret: import.meta.env.TURNSTILE_SECRET_KEY,
response: formData.captchaToken
})
});
if (!verifyResponse.ok) {
return new Response(JSON.stringify({ error: 'Captcha failed' }), { status: 400 });
}
// メール送信等の処理
return new Response(JSON.stringify({ success: true }));
}
3. HubSpot 埋め込みの React Island化
// React コンポーネント
export default function HubSpotForm() {
useEffect(() => {
if (window.hbspt) {
window.hbspt.forms.create({
region: 'na1',
portalId: '...',
formId: '...',
target: '#hubspot-form'
});
}
}, []);
return <div id="hubspot-form" />;
}
Astro での使用:
<HubSpotForm client:load />
メリット
- CF Pages はサーバーレス、メンテナンス不要
- Turnstile は無料+プライバシー優先
- Astro + React Island で必要な部分のみインタラクティブ
検討点
- Firebase から CF Functions への認証情報移行
- メール送信ロジックの Durable Objects 利用の検討
- ログ保存先の決定(D1 / Firestore など)
概要
reCAPTCHA v3 から Cloudflare Turnstile へ移行する際の実装パターン。Turnstile は無料・プライバシーフレンドリーで CF Pages ネイティブ対応。
キー情報
- テストキー
- サイトキー:
1x00000000000000000000AA - シークレット:
1x0000000000000000000AA
- サイトキー:
- 本番環境: Cloudflare Dashboard で生成
クライアント側(React/Svelte)
// CDN 読み込み(head に挿入)
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
// Explicit rendering(推奨)
turnstile.render('#turnstile-container', {
siteKey: import.meta.env.PUBLIC_TURNSTILE_KEY,
theme: 'light',
callback: (token) => {
// トークン取得後の処理(フォーム送信など)
console.log('Turnstile token:', token);
}
});
// リセット(フォーム送信後)
turnstile.reset();
サーバー側(CF Pages Functions / Node.js)
// POST /api/verify-captcha
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: env.TURNSTILE_SECRET_KEY,
response: token
})
});
const data = await response.json();
if (!data.success) {
throw new Error('Captcha verification failed');
}
メリット
- reCAPTCHA より実装が簡単
- ユーザー側のプライバシー尊重(データ共有なし)
- CF Pages との相性が良い
- 無料で使用可能