0
Help us understand the problem. What are the problem?

posted at

HTMLメール配信サービスを作ってみた

こちらは、ゆる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 の初期画面が起動することを確認できます。

image.png

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

メールの本文として、果物の名称の配列を受け取ると、リストとしてレンダリングするとします。

MailBody.tsx
export const MailBody = (fruits: string[]) => {
  return (
    <>
      <ul>
      {
        fruits.map((fruit: string, index: number) => (
          <li key={ index }>{ fruit }</li>
        ))
      }
      </ul>
    </>
  )
};

メール本文のコンポーネントができたらレンダリングを行います。
レンダリングは rederToString 関数で行います。
sendmail.ts は以下のようになります。

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メールを動的に構築して配信するところまでやったので良しとはしていますが、普通が知りたい。。。

ここまでお付き合いしていただきありがとうございます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
0
Help us understand the problem. What are the problem?