Netlify Functionsを使った問い合わせフォームの作り方
Netlifyには簡単に問い合わせフォームを導入できる機能がありますが、便利に導入できる反面、カスタマイズ性が乏しい、無料プランは月に10MBしかファイルを添付できない、自動返信メール機能がない、などの無視できない制約があります。
しかし、Netlify Functionsの機能を使うことで、上記のデメリットがない使い勝手の良い問い合わせフォームを作ることができます。
以下、添付ファイルありの問い合わせフォームの作り方をご紹介します。
Netlify Functionsフォルダにエンドポイントを作る
フォームが送信された時に発火する特別なイベント「submission-created」があり、このイベントを使うのが正攻法なのですが、実行環境で動作が変わったり、特有の「所作」が面倒なので、オリジナルのエンドポイントを作ります。
例として、functionsフォルダ内に「contact-form」フォルダを作り、その中にcontact-form.tsを作成します。階層は以下の通りです。
netlify
└── functions
└── contact-form
└── contact-form.ts
この場合、エンドポイントは.netlify/functions/contact-form
になり、ここにデータをpostすることになります。ファイルの中身は後述します。
サイト側のフォーム部分
Netlify Formと違い、専用の属性やタグをフォーム部分に追加する必要はありません。通常のFormと同じ構成で大丈夫です。
私は、非同期の処理が好きなので、送信ボタン押下時にaxiosでpost送信するようにしています。構成は以下の通りです。
return axios("/.netlify/functions/contact-form", {
method: "post",
data: this.formData
})
.then(() => {
// 成功した時の処理
})
.catch(error => {
// 失敗した時の処理
})
}
このコードで重要なポイントは、FormData(multipart/form-data)で送っていないことです。画像をBase64エンコードした上で他のフィールドと同じく文字列でpostしています。
busboyで画像データのパースが上手くいかなかったのが理由です。
メール送信のイベントを作る
先ほど作成したcontact-form.ts
ファイルに、postされたデータを受け取ってメールを送信するイベントの処理を記述します。
postされたデータは第1引数のevet
の中のbody
に格納されていて、それをパースして使います。メールの送信にはNodemailerを使います。
サンプルコードは以下の通りです。
// contact-form.ts
import { Handler } from "@netlify/functions";
import { createTransport } from "nodemailer";
interface formFields {
name: string;
email: string;
message: string;
attachment: {
name: string;
url: string;
};
}
export const handler: Handler = async event => {
const { name, email, attachment }: formFields = JSON.parse(event.body!);
const transporter = createTransport({
host: "mail.example.com",
port: 465,
secure: true,
auth: {
user: process.env.MAIL_AUTH_USER,
pass: process.env.MAIL_AUTH_PASS
}
});
const from = '"Shinobi Works" <[email protected]>';
try {
// 問い合わせの通知メールを送信
await transporter.sendMail({
from,
to: "[email protected]",
replyTo: email,
subject: "問い合わせがありました",
attachments: attachment && [
{ filename: attachment.name, path: attachment.url }
],
text: `<名前>\n${name}\n<問い合わせ内容>\n${message}`
});
} catch (error) {
console.log(`メール送信に失敗しました`);
throw error;
}
// 自動返信メール
await transporter.sendMail({
from,
to: email,
subject: "【Shinobi Works】お問い合わせいただきありがとうございます",
text: `${name}様\nこの度は...(略)`
});
return {
statusCode: 200,
body: "Successed to send an email"
};
};
Base64エンコードされた画像はそのままpathに指定することができます。その他のコードについてはNodemailderの使い方の記事をご覧ください。