問い合わせフォームの作成
Next.jsでNodeMailerを使ってコンタクトフォームを作成していく。Vercelにアップロードしたいけど、何やらできないみたいな記事をいくつか見かけたが全然できる。公式でもNodemailerは使えると書いてある。ただPostmarkやResend、SendGridやAWSSESなどのサードパーティーメールサービスを使用した方が分析等しやすいためおすすめとは書かれている。
前提
next.js14
npx create-next-app
typescriptなどは全部yes
nodemailerをインストール
npm install nodemailer @types/nodemailer
コードを書いていく
my-appフォルダ配下(.gitignoreと同列)に.env.localファイルを作成して
NODEMAILER_EMAIL="aaaaaaa.gmail.com"
NODEMAILER_PASSWORD="bbbb bbbb bbbb bbbb"
NODEMAILER_EMAIL、NODEMAILER_PASSWORDは任意の文字で大丈夫だがAPIの呼び出し(route.ts)に合わせる必要がある。なお今回はgmailを使用する。
NODEMAILER_EMAILはGoogleアカウントのメールアドレスを使用。
NODEMAILER_PASSWORDはGoogleアカウントを検索し→セキュリティ→2段階認証プロセス→アプリ パスワードで新しく作成したものを使用する。
そんなわけでフロントエンド部分
npx create-next-appでいならないところを削除して簡易的なものを作成import ContactForm from "./components/ContactForm"
const page = () => {
return (
<main className="flex grow flex-col items-center justify-center">
<div className="space-y-4">
<h1 className="text-4xl">Contact Us</h1>
<ContactForm />
</div>
</main>
)
}
export default page
上記のContactFormはcomponentsで別途作成して呼び出している
"use client"
import { useState } from "react"
function ContactForm() {
const [email, setEmail] = useState("")
const [message, setMessage] = useState("")
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
await fetch("/api/email", {
method: "POST",
body: JSON.stringify({ email, message }),
})
}
return (
<div>
<h2>Contact Us</h2>
<form onSubmit={handleSubmit} className="space-y-10">
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label>Message:</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
required
></textarea>
</div>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default ContactForm
Apiを実装していく
Nodemailerのexampleは参考までに
app/api/email/route.tsにapiを作成していく
なおNext.js13以降でApiを使用する場合デフォルト値はapp/api/任意の文字/route.tsで、もし色々なApiを実装したいならばapiの配下にApiのフォルダが沢山作っていく感じ。それぞれのページングする際にディレクトリにpage.tsxを各々に作っていく感じと似たようなものと覚えておこう。
import { NextResponse, type NextRequest } from "next/server"
import nodemailer from "nodemailer"
import Mail from "nodemailer/lib/mailer"
import { env } from "process"//.envからの呼び出し
export async function POST(request: NextRequest) {
const { email, message } = await request.json()
const transport = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.NODEMAILER_EMAIL,
pass: process.env.NODEMAILER_PASSWORD,
},
})
const mailOptions: Mail.Options = {
from: env.NODEMAILER_EMAIL,
to: env.NODEMAILER_EMAIL,
subject: `Message from お問い合わせ(${email})`,
text: message,
}
try {
await transport.sendMail(mailOptions)
return NextResponse.json({ message: "Success!", status: 200 })
} catch (err) {
return NextResponse.json({ message: "Failed!", status: 500 })
}
}
メインページpage.tsxのsubmitボタンを押すとapiのemailが呼び出される。
参考までにApiを叩いたものの確認の仕方
apiの呼び出し方。jsonを返すようなAPIを作成localhost:3000/api/helloでapiを呼び出すことができるようにしたい。
import { NextResponse } from "next/server"
export async function GET() {
return NextResponse.json("Hello!")//({name:"tanaka"})なども使える
}
export default function hello(req, res){
return res.json("Hello!")
}
参照ページ
メール送信後(API呼出後)ページ遷移する方法
呼び出した後の処理はbuttonタグの中にonClickイベントを追加、useRouterを使用してrouter.pushすることでページ遷移が可能になる.参照は以下サイト
"use client"
import { useRouter } from "next/navigation"
import { useState } from "react"
function ContactForm() {
const router = useRouter();
const [email, setEmail] = useState("")
const [message, setMessage] = useState("")
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
await fetch("/api/email", {
method: "POST",
body: JSON.stringify({ email, message }),
})
}
return (
<div>
<h2>Contact Us</h2>
<form onSubmit={handleSubmit} className="space-y-10">
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label>Message:</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
required
></textarea>
</div>
<button type="submit" onClick={()=>router.push("/page")}>Submit</button>
</form>
</div>
)
}
export default ContactForm
現在/pageは作成していないのでページエラーが出る。適当にページを作ってね。
ハマった所として下記のようにしようとしたら失敗した
NextRouter was not mounted.
import { useRouter } from "next/router";
export default function IndexPage() {
const router = useRouter(); //useRouterフックを定義して
return (
<div>
Hello World.{" "}
<button onClick={() => router.push("/about")}> // router.pushで遷移したいページを指定するだけ
go to about
</button>
</div>
);
}
上記はuseRouterをnext/routerではなくnext/navigationからuseRouterをimportすることで、エラーが解消されれる。また、URLクエリパラメータを取得するにはuseRouterではなくuseSearchParamsをimportする必要がある。
Vercelでの環境設定
上記で作成したコードはhttp://localhost:3000/で実行できたと思う。Submitボタンを押せばメールが送信される。なおメールの発信(From)は自分で設定したGoogleアカウントのメールアドレスからの発信となることを覚えておこう。(届いたメールを安易に返信しても自分に届くだけである)さて本題のVercelの設定だが、コードをデプロイして環境設定をするくらいである。環境設定の仕方は至極簡単。
先程作成した.env.localファイルをEnvironment Variablesに設定する。SettingにEnvironment Variablesの項目があるので
NODEMAILER_EMAIL="sukeve@gmail.com"
NODEMAILER_PASSWORD="bskx zchv "
上記のようなenvファイルなら(env.localなどもOK)
KeyにNODEMAILER_EMAIL Valueにsukeve@gmail.comと
keyにNODEMAILER_PASSWORD Valueにbskx zchvを記入すればよい。
恐らくNext.jsの更新によりNodemailerが使用できなかったのが使用できるようになったのでしょう。是非とも問い合わせフォームを自作できるようにしていこう