



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









生徒先生、静的サイトでお問い合わせフォームを実装したいんですけど、バックエンドサーバーがないとフォーム送信できませんよね…どうすればいいですか?



よーく聞くんだぞ。今日はサーバーレス関数を使ったフォーム処理について詳しく解説するぞい!静的サイトでも簡単にフォーム機能を実装できるんじゃ!



そう言う方法があるんですね!はい!お願いします!
Jamstackサイトは、高速でセキュアなWebサイトを構築できる一方で、動的な処理が必要なフォーム送信では課題が発生します。従来のサーバーサイド処理が使えないため、フォームデータの受信やメール送信を別の方法で実装する必要があります。
そこで活用するのが、サーバーレス関数(Serverless Functions)です。サーバーレス関数を使えば、サーバーを管理することなく、フォーム処理を実装できます。
本記事では、Jamstackサイトでのフォーム処理をサーバーレスで実装する方法を、複数のプラットフォーム(Netlify、Vercel、AWS Lambda)を使った実装例とともに詳しく解説します。
「学習→案件獲得」につなげた受講生のリアルな体験談も公開中!
働き方を変えたい方にも響くストーリーです。
菅井さん
将来的への不安と子育てという背景から「副業」に挑戦しようと決意。独学からプログラミングの学習を開始していたが、WithCodeに出会い体験コースを受講。約4ヶ月の学習に取り組み、当初の目標であった卒業テスト合格を実現した。WithCode Platinumにて3件の案件を担当し、現在は副業だけでなく本格的に「フリーランス」として在宅で活躍していきたいと考えるようになる。
詳しくはこちらの記事をご覧ください。


菅井さんの主な制作実績はこちら


Jamstack(ジャムスタック)は、JavaScript、API、Markupの頭文字を取った、モダンなWeb開発アーキテクチャです。従来のサーバーサイドレンダリングではなく、事前にHTMLを生成(Pre-rendering)し、CDNで配信することで、高速で安全なWebサイトを実現します。





静的サイトだと、なぜフォーム処理が難しいんですか?



静的サイトにはバックエンドサーバーがないから、フォームデータを受け取る仕組みを別途用意する必要があるんじゃ!
従来のWebサイトでは、PHPやNode.jsなどのサーバーサイド言語でフォームデータを受け取り、処理していました。
// 従来のPHPでのフォーム処理例
<?php
if ($_POST['name'] && $_POST['email']) {
$name = $_POST['name'];
$email = $_POST['email'];
mail('admin@example.com', 'お問い合わせ', $message);
}
?>しかし、Jamstackサイトは静的ファイルのみで構成されるため、このような処理ができません。
Jamstackサイトでフォーム処理を実現する方法は、主に以下の3つです。
次では、サーバーレス関数を使った実装方法を中心に解説します。


サーバーレス関数(Serverless Functions)は、サーバーを管理せずに実行できる関数です。必要なときだけ実行され、使った分だけ課金される仕組みで、運用コストが低いのが特徴です。
| プラットフォーム | サービス名 | 言語 | 無料枠 |
|---|---|---|---|
| Netlify | Netlify Functions | JavaScript、TypeScript、Go | 125,000リクエスト/月 |
| Vercel | Serverless Functions | JavaScript、TypeScript、Go、Python、Ruby | 100GB-時間/月 |
| AWS | Lambda | Node.js、Python、Go、Java、C#など | 100万リクエスト/月 |
| Firebase | Cloud Functions | JavaScript、TypeScript | 200万呼び出し/月 |





Netlify Functionsってなんですか?



Netlify Functionsは、Netlifyが提供するサーバーレス関数サービスじゃ。デプロイが簡単で、初心者にも扱いやすいのが特徴じゃ!
my-jamstack-site/
├── public/
│ └── index.html
├── netlify/
│ └── functions/
│ └── form-handler.js
└── netlify.toml<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>お問い合わせフォーム</title>
<style>
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
}
h1 {
margin-bottom: 24px;
}
#contact-form {
display: flex;
flex-direction: column;
gap: 24px;
max-width: 500px;
}
/* 各フィールド */
#contact-form > div {
display: flex;
flex-direction: column;
gap: 8px; /* ラベルと入力欄の間隔 */
}
label {
font-weight: 600;
}
input,
textarea {
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 16px;
}
button {
padding: 14px;
border: none;
border-radius: 8px;
background: #0066cc;
color: white;
font-size: 16px;
cursor: pointer;
transition: background 0.2s ease;
}
button:hover {
background: #004999;
}
</style>
</head>
<body>
<h1>お問い合わせ</h1>
<form id="contact-form">
<div>
<label for="name">お名前</label>
<input type="text" id="name" name="name" required>
</div>
<div>
<label for="email">メールアドレス</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="message">お問い合わせ内容</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">送信</button>
</form>
<script>
const form = document.getElementById('contact-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const data = Object.fromEntries(formData);
try {
const response = await fetch('/.netlify/functions/form-handler', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (response.ok) {
alert('送信が完了しました!');
form.reset();
} else {
alert('送信に失敗しました。もう一度お試しください。');
}
} catch (error) {
console.error('Error:', error);
alert('エラーが発生しました。');
}
});
</script>
</body>
</html>// netlify/functions/form-handler.js
const nodemailer = require('nodemailer');
exports.handler = async (event, context) => {
// CORSヘッダーの設定
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
};
// プリフライトリクエストへの対応
if (event.httpMethod === 'OPTIONS') {
return {
statusCode: 200,
headers,
body: '',
};
}
// POSTメソッドのみ許可
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
headers,
body: JSON.stringify({ error: 'Method Not Allowed' }),
};
}
try {
// リクエストボディのパース
const { name, email, message } = JSON.parse(event.body);
// バリデーション
if (!name || !email || !message) {
return {
statusCode: 400,
headers,
body: JSON.stringify({ error: '必須項目が入力されていません' }),
};
}
// メール送信の設定
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
// メール内容
const mailOptions = {
from: process.env.SMTP_USER,
to: 'admin@example.com',
subject: `【お問い合わせ】${name}様より`,
text: `
お名前: ${name}
メールアドレス: ${email}
お問い合わせ内容:
${message}
`,
html: `
<h2>お問い合わせがありました</h2>
<p><strong>お名前:</strong> ${name}</p>
<p><strong>メールアドレス:</strong> ${email}</p>
<p><strong>お問い合わせ内容:</strong></p>
<p>${message.replace(/\n/g, '<br>')}</p>
`,
};
// メール送信
await transporter.sendMail(mailOptions);
return {
statusCode: 200,
headers,
body: JSON.stringify({ message: '送信が完了しました' }),
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
headers,
body: JSON.stringify({ error: 'サーバーエラーが発生しました' }),
};
}
};{
"name": "jamstack-form",
"version": "1.0.0",
"dependencies": {
"nodemailer": "^6.9.0"
}
}Netlifyの管理画面で、以下の環境変数を設定します。
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
ポイント: Gmailを使う場合は、アプリパスワードを生成して使用します。





Vercelはどんな機能があるんでしょうか?



Vercelは、Next.jsとの統合が強力で、TypeScriptのサポートも充実しているぞい!
my-jamstack-site/
├── public/
│ └── index.html
├── api/
│ └── form-handler.ts
└── package.json// api/form-handler.ts
import type { VercelRequest, VercelResponse } from '@vercel/node';
import nodemailer from 'nodemailer';
export default async function handler(
req: VercelRequest,
res: VercelResponse
) {
// CORSヘッダーの設定
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// プリフライトリクエスト
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
// POSTメソッドのみ許可
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method Not Allowed' });
}
try {
const { name, email, message } = req.body;
// バリデーション
if (!name || !email || !message) {
return res.status(400).json({ error: '必須項目が入力されていません' });
}
// メールアドレスの形式チェック
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({ error: 'メールアドレスの形式が正しくありません' });
}
// メール送信設定
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
// メール送信
await transporter.sendMail({
from: process.env.SMTP_USER,
to: 'admin@example.com',
subject: `【お問い合わせ】${name}様より`,
text: `
お名前: ${name}
メールアドレス: ${email}
お問い合わせ内容:
${message}
`,
});
return res.status(200).json({ message: '送信が完了しました' });
} catch (error) {
console.error('Error:', error);
return res.status(500).json({ error: 'サーバーエラーが発生しました' });
}
}
// フロントエンドからの送信
const response = await fetch('/api/form-handler', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});




コードを書かずにフォームを実装する方法はありませんか?



もちろんあるぞい!Netlify FormsやFormspreeなどのサービスを使えば、コード不要でフォームが実装できるんじゃ!
Netlifyが提供する標準のフォーム機能です。HTMLにnetlify属性を追加するだけで使えます。
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<div>
<label for="name">お名前</label>
<input type="text" id="name" name="name" required>
</div>
<div>
<label for="email">メールアドレス</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="message">お問い合わせ内容</label>
<textarea id="message" name="message" required></textarea>
</div>
<button type="submit">送信</button>
</form>Formspreeは、外部のフォーム送信サービスです。任意のサイトで使用できます。
<form action="https://formspree.io/f/YOUR_FORM_ID" method="POST">
<label for="name">お名前</label>
<input type="text" name="name" required>
<label for="email">メールアドレス</label>
<input type="email" name="_replyto" required>
<label for="message">お問い合わせ内容</label>
<textarea name="message" required></textarea>
<button type="submit">送信</button>
</form>


フロントエンドとバックエンドの両方でバリデーションを実装します。
// サーバー側のバリデーション例
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function validateInput(data) {
if (!data.name || data.name.length > 100) {
throw new Error('お名前は100文字以内で入力してください');
}
if (!validateEmail(data.email)) {
throw new Error('メールアドレスの形式が正しくありません');
}
if (!data.message || data.message.length > 1000) {
throw new Error('お問い合わせ内容は1000文字以内で入力してください');
}
}同一IPアドレスからの連続送信を制限します。
// 簡易的なレート制限の実装例
const rateLimit = new Map();
function checkRateLimit(ip) {
const now = Date.now();
const windowMs = 60 * 1000; // 1分
const maxRequests = 5; // 1分間に5回まで
if (!rateLimit.has(ip)) {
rateLimit.set(ip, []);
}
const requests = rateLimit.get(ip).filter(time => now - time < windowMs);
if (requests.length >= maxRequests) {
throw new Error('送信回数が多すぎます。しばらくしてから再度お試しください。');
}
requests.push(now);
rateLimit.set(ip, requests);
}GoogleのreCAPTCHA v3を導入し、ボット対策を強化します。
<!-- フロントエンド -->
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
<script>
async function handleSubmit(e) {
e.preventDefault();
// reCAPTCHAトークン取得
const token = await grecaptcha.execute('YOUR_SITE_KEY', { action: 'submit' });
const formData = new FormData(form);
const data = Object.fromEntries(formData);
data.recaptchaToken = token;
// サーバーに送信
await fetch('/api/form-handler', {
method: 'POST',
body: JSON.stringify(data),
});
}
</script>// サーバー側でreCAPTCHAトークンを検証
async function verifyRecaptcha(token) {
const response = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`,
});
const data = await response.json();
if (!data.success || data.score < 0.5) {
throw new Error('reCAPTCHA検証に失敗しました');
}
}APIキーやパスワードは環境変数で管理し、コードに直接書かないようにします。





Jamstackフォーム処理を対応するにあたってトラブルシューティングのことも知りたいです!



うむ。ここでは、主なトラブルシューティングを3つ紹介するぞい!
原因と解決法:
原因と解決法:
Access-Control-Allow-Originヘッダーを追加原因と解決法:



Jamstackサイトでも、サーバーレス関数を使えば簡単にフォームが実装できるんですね!



その通りじゃ!まずはNetlify FormsやFormspreeなどのサービスから始めて、カスタマイズが必要になったらサーバーレス関数を実装するのがおすすめじゃぞ!



はい!次のプロジェクトでNetlify Functionsを使ったフォーム処理に挑戦してみます!ありがとうございました!
本記事では、Jamstackサイトのフォーム処理をサーバーレスで実装する方法について、複数のプラットフォームを使った実装例とともに詳しく解説しました。
重要なポイントは以下の通りです。
・Jamstackの課題:静的サイトではバックエンド処理ができないため、サーバーレス関数が必要
・実装方法:Netlify Functions、Vercel Serverless Functions、フォーム送信サービスの3つ
・推奨方法:初心者はNetlify FormsやFormspree、カスタマイズが必要ならサーバーレス関数
・セキュリティ対策:バリデーション、レート制限、reCAPTCHA、環境変数管理が重要
・選定基準:プロジェクトの規模、カスタマイズ性、予算に応じて最適な方法を選択
WithCodeで学んだHTML・CSS・JavaScriptの基礎知識に、サーバーレス関数の技術を組み合わせれば、どんな実案件でも対応できます。
Jamstackサイトのフォーム処理は、サーバーレス関数を使えばシンプルに実装できます。ぜひ実際のプロジェクトで活用し、高速で安全なWebサイトを構築してください。


副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?
未経験でも心配することありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!
公式サイト より
今すぐ
無料カウンセリング
を予約!