こちらは、ゆるWeb勉強会@札幌 Advent Calendar 2021 の2日目の記事です。
はじめに
ちょっと会社でDBからデータを取得しメールで配信するシステムを開発することになりました。
配信するメールは、プレーンテキストのメールではなく、テーブルで情報をまとめたHTMLメールを配信するシステムです。
HTMLメールを配信するシステムをどのように組めばいいかよくわからなかい状態で、HTMLを構築すればいいんだよね、バックエンドでHTMLを構築するならSSRだよねということで、自分の中で短絡的にReactか?ということで、Next.jsを使用して作っています。
この辺「いや、そうじゃないだろう」というツッコミをいただければと思い記事にしました。
一応ソースはこちらにあります。
環境構築
まずはNext.jsの環境を作成します。
プロジェクト名は mail-publish-example
とします。
$ yarn create next-app mail-publisher-example --typescript
とりあえずここで起動することを確認します。
$ cd mail-publish-example
$ yarn dev
http://localhost:3000 にアクセスすることで
Next.js の初期画面が起動することを確認できます。
APIを作ってみる
システムの構成としてはweb apiが呼ばれるとデータを取得しメール本文を生成して配信します。
というところでAPIを作ってみます。
apiの構成
generator で作成したプロジェクトのソースは pages
ディレクトリ以下に格納されています。
pages
├── _app.tsx
├── api
│ └── hello.ts
└── index.tsx
api を実装するには pages/api
以下に作成したいapi名のファイルを格納します。
今回は sendmail
というapiを作りたいと思います。
apiの実装
まずは sendmail.ts
ファイルをapiディレクトリ以下に作成します。
pages
├── _app.tsx
├── api
│ ├── hello.ts
│ └── sendmail.ts
└── index.tsx
hello.ts
を参考にapiを実装します。
ほぼそのまんまのコードですが、新規に作ったものが動くのに意味があると思うので、リネームではなく新規に作成しました。
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
status: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
console.log("Hello sendmail api !");
res.status(200).json({ status: 'OK' })
}
ここで新規に作成したAPIが動くかどうかを確認します。
yarn dev
で動かして、curl でPOSTしてみます。
ちゃんとコンソール上にも Hello sendmail api !
が出力され、レスポンスも返ってきたことが確認できました。
$ curl -XPOST http://localhost:3000/api/sendmail
{"status":"OK"}
ここからreactを使ってHTMLの本文を作成していきます。
メール本文を作成
メール本文のコンポーネントは MailBody.tsx
とします。
pages
├── _app.tsx
├── api
│ ├── hello.ts
│ ├── MailBody.tsx
│ └── sendmail.ts
└── index.tsx
メールの本文として、果物の名称の配列を受け取ると、リストとしてレンダリングするとします。
export const MailBody = (fruits: string[]) => {
return (
<>
<ul>
{
fruits.map((fruit: string, index: number) => (
<li key={ index }>{ fruit }</li>
))
}
</ul>
</>
)
};
メール本文のコンポーネントができたらレンダリングを行います。
レンダリングは rederToString
関数で行います。
sendmail.ts
は以下のようになります。
import type { NextApiRequest, NextApiResponse } from 'next';
import { renderToString } from "react-dom/server";
import { MailBody } from "./MailBody";
type Data = {
status: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const fruits = [
"apple",
"banana",
"orange",
];
const body = renderToString(MailBody(fruits));
console.log("body", body);
res.status(200).json({ status: 'OK' })
}
実行すると、以下のような文字列がbody変数に格納されているはずです。
<ul data-reactroot=""><li>apple</li><li>banana</li><li>orange</li></ul>
これで、apiがコールされるとHTMLが動的に構築することができました。
ここまでできると、あとはnodemailerにわたすだけでメールが送信できます。
メールを送信する。
nodemail をインストールする
以下のコマンドを実行し、nodemailerを追加します。
$ yarn add nodemailer
メールサーバの情報を作成します。
const transporter = createTransport({
host: "MAIL SERVER ADDRESS",
port: 465,
secure: true,
auth: {
user: "ACCOUNT NAME",
pass: "PASSWORD",
}
});
メールサーバの情報を作成しましたら、以下で送信します。
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const fruits = [
"apple",
"banana",
"orange",
];
const body = renderToString(MailBody(fruits));
try {
transporter.sendMail({
from: "hogehoge@examile.com",
to: "hogehoge2@example.com",
subject: "mail example",
html: body,
});
res.status(200).json({ status: 'OK' })
} catch (error) {
console.error("fail to send mail.", error);
res.status(500).json({ status: 'NG' })
}
}
メールサーバの設定が正しく行えていれば、 to
で設定した宛先にメールが届いているはずです。
よくわからないけど、とりあえずHTMLメールを動的に構築して配信するところまでやったので良しとはしていますが、普通が知りたい。。。
ここまでお付き合いしていただきありがとうございます。