Nextjs・Firebase・Nodemailerでお問い合わせフォームを作る
はじめに
Nextjs と Firebase を使って作成した Web サイトにお問い合わせさフォームを作成していたのですが、Nextjs Api Routes を使っていて実現するのにハマってしまったのでハンズオン形式で実現の方法をまとめます
ここでのハンズオンの内容は以下のリポジトリにあります
学べること
Cloud Functions for Firebase を使った Nodemailer のデプロイ
環境
- VSCode
- Firebase tools 12.4.7
Firebase への Nextjs のデプロイ、GCP のプロジェクト用意は各自行ってください(firebase login も含む)
npx などの nextjs 作成に関わるものもインストール済みとして進めます
1. Nextjs のプロジェクト準備
まずは問い合わせフォームを作成します
ここでは特にバリデーションなどは行わず、入力された内容をそのまま送信するだけのフォームを作成します
$ npx create-next-app contact-form
> Typescript Yes
> ESLint Yes
> Tailwind Yes
> src/ directory No
> App Router Yes
> import alias No
$ cd contact-form
// VSCodeで開く
2. フォームの作成
Tailwind を使ってそれっぽくフォームを作成します
useState
などは説明しませんのでわからない方はググってください
"use client";
import { SyntheticEvent, useState } from "react";
export default function Home() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async (e: SyntheticEvent) => {
e.preventDefault();
// ここでメール送信を行う
};
return (
<div className="min-h-screen flex justify-center items-center bg-gray-100">
<div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl mb-4">お問い合わせ</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-600">
名前
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-600">
メールアドレス
</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-600">
内容
</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
className="mt-1 p-2 w-full border rounded-md h-32"
required
/>
</div>
<button
type="submit"
className="w-full p-2 text-white bg-blue-500 hover:bg-blue-600 rounded-md"
>
送信
</button>
</form>
</div>
</div>
);
}
それでは起動して画面を確かめます
$ npm run dev
http://localhost:3000にアクセスします
フォームが表示されました
2. Nodemailer でメール送信をできるようにする
次は Nodemailer を使って、問い合わせ内容を自分宛てにメール送信できるようにします
ここでは Gmail を利用してメール送信を行います
2-1. 注意
ここで私は Nextjs api routes を利用して/app/api にメール送信用のエンドポイントを作成していました
しかし、これでfirebase deploy
してもうまくエンドポイントが利用できませんでした
なので今回はcloud functions for firebase
を利用してエンドポイントを作成します
この機能を利用するにはBlaze
プランにする必要があります
無料枠で行うことはできないので注意してください
2-2. Cloud Functions の作成
作成したcontract-form
の上の階層に戻ってもう一つプロジェクトを作成します
$ cd ..
$ mkdir contact-mailer
$ cd contract-mailer-functions
$ firebase init functions
> メールアドレス、プロジェクトは用意したものを選択
> Typescript Yes
> Lint Yes
> npm now Yes
cd functions
// vscodeで開く
これで Fireabse を利用して簡単に関数をデプロイできます
2-3. Nodemailer のインストール
nodemailer を利用するのでインストールを行います
$ npm install nodemailer cors
$ npm install --save-dev @types/nodemailer
CORS の対応も必要になるので一緒に入れました
2-4. アプリからのアクセス許可
Gmail でアプリからのアクセスを許可する必要があります
利用する Gmail のアカウントで以下の設定を行ってください
メニューから「アカウント」をクリック
左メニューから「セキュリティ」をクリック
ここで「2 段階認証」が有効になっているか確認します
なっていない場合は有効に設定しましょう(この例ではなっていません)
認証が済んだら、一番下の「アプリパスワード」の>をクリック
アプリ名を適当に入れて「作成」をクリック
パスワードが画面に表示されるのでメモっておきましょう
2-5. メール送信用の関数を作成
メール送信用の関数を用意します
わからないところはドキュメントをみてくださればと思います
まずは環境変数に先程のアプリパスワードとメールアドレスを設定していきます
$ touch .env
GMAILUSER=メールアドレス
GMAILPASSWORD=アプリパスワード
次に関数を実装していきます
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import * as nodemailer from "nodemailer";
import * as cors from "cors";
// eslint-disable-next-line object-curly-spacing
const corsHandler = cors({ origin: true });
admin.initializeApp();
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 587,
auth: {
user: process.env.GMAILUSER,
pass: process.env.GMAILPASSWORD,
},
});
exports.sendNodeMail = functions.https.onRequest(
(req: functions.Request, res: functions.Response) => {
corsHandler(req, res, () => {
const body = req.body;
const toHostMailData = {
from: body.email,
to: "あなたのメールアドレス",
subject: `【お問い合わせ】${body.name}様からのお問い合わせ`,
text: `${body.message}`,
html: `
<p>お問い合わせ内容</p>
<p>お名前:${body.name}</p>
<p>メールアドレス:${body.email}</p>
<p>お問い合わせ内容:${body.content}</p>
`,
};
return transporter.sendMail(toHostMailData, (error, info) => {
if (error) {
console.log(error);
res.send("error");
} else {
console.log("Message sent: " + info.response);
res.send("success");
}
});
});
}
);
メールアドレスの箇所は自分のメールアドレスに変更してください
ポイントは最後にres.send
を行っていることです。このあとフロント側でレスポンスに応じてアラートを実行するのですが、この処理がないと永遠に待ち状態になってしまいます
// eslint-disable-next-line object-curly-spacing
また functions はデフォルトではus-central1
のリージョンにデプロイを行います。functions.region('asia-east1')
のようにリージョンを指定することもできます
2-6. デプロイ
contract-mailer-function
の階層に戻ってコマンドを実行します
$ firebase deploy
デプロイが完了されたらGCP
のコンソールからCloud Functions
を開きます
sendNodeMail
をクリックして「トリガー」から「トリガー URL」をコピーします
2-7. フロント側の実装
contact-form
に戻ってメール送信を実行するように変更します
"use client";
import { SyntheticEvent, useState } from "react";
export default function Home() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async (e: SyntheticEvent) => {
e.preventDefault();
fetch("あなたのsendNodeMailのURL", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: name,
email: email,
content: message,
}),
}).then((res) => {
if (res.status === 200) {
alert("送信が完了しました。");
setName("");
setEmail("");
setMessage("");
} else {
alert("送信に失敗しました。");
}
});
};
return (
<div className="min-h-screen flex justify-center items-center bg-gray-100">
<div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl mb-4">お問い合わせ</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-600">
名前
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-600">
メールアドレス
</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-600">
内容
</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
className="mt-1 p-2 w-full border rounded-md h-32"
required
/>
</div>
<button
type="submit"
className="w-full p-2 text-white bg-blue-500 hover:bg-blue-600 rounded-md"
>
送信
</button>
</form>
</div>
</div>
);
}
handleSubmit
にメール送信の処理を追加しました
あなたの sendNodeMail の URL の部分は先程コピーした URL に変更してください
2-8. 動作確認
実際に画面から送信してみます
すべての項目をうめて送信ボタンを押します
すると、アプリパスワードを設定したアカウントにメールが届いているはずです
おわりに
今回は Nextjs と Firebase を使って問い合わせフォームを作成しました
Nextjs と Firebase でどのように実現するか迷ったので参考になると嬉しいです
Discussion