この記事では、Express.jsアプリケーションでAWS SES(Simple Email Service)を使用して自動返信メール機能を実装する手順を説明する。

参照ソース: https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v3/developer-guide/javascript_ses_code_examples.html#:r3l:-trigger

全体像

  1. AWSコンソール > SESでの設定
  2. AWSコンソール > IAMユーザーの作成とアクセスキーの取得
  3. 実装: パッケージのインストール
  4. Contactモデルの作成とルート設定
  5. ExpressとSESの連携を実装
  6. メール送信処理(コントローラー)の実装

以下の構成に従って、実装する。

AWSコンソールでの設定

SESの有効化とドメイン/メールアドレスの検証

AWS SESにアクセス: SESコンソールにアクセスする。
左側のナビゲーションバーで「ID」を選択し、「IDの作成」をクリック。

メールアドレスを選択し管理者のメールアドレスを登録する。
※受信テスト用のメールアドレスも登録しておく。

検証メールが送信されるので、メール内のリンクをクリックして検証を完了する。

次にドメインの検証を行う。
同じように「IDの作成」をクリックし、次は「ドメイン」を選択して該当のドメインを検証する。

検証が正常に完了したらこの手順は完了。

IAMユーザーの作成とアクセスキーの取得

IAMコンソールにアクセスし、「ユーザーを追加」をクリックする。
ユーザー名を入力し、次へ。

AmazonSESFullAccessのポリシーをアタッチする。

ユーザーの作成が完了すると、アクセスキーIDとシークレットアクセスキーを取得できるので、保持しておく。

パッケージのインストール

プロジェクトディレクトリで以下のコマンドを実行して、SDKをインストールします。
dotenv express mongooseなどはすでにインストールされている前提で進める。

参考記事: 【Express/MongoDB/Docker】DB接続、データ登録・取得手順

$ npm install @aws-sdk/client-ses
"@aws-sdk/client-ses": "^3.577.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"mongoose": "^8.3.2",

Contactモデルの作成とルート設定

Contactモデルの作成

src/model/Contact.ts

import mongoose, { Document } from "mongoose";

export interface IContact extends Document {
  name: string;
  email: string;
  detail: string;
}

const contactSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
    },
    detail: {
      type: String,
      required: true,
    },
  },
  {
    timestamps: true,
  }
);

const Contact = mongoose.model<IContact>(
  "Contact",
  contactSchema,
  // 第三引数にMongoDBのコレクション名を指定
  "contacts"
);

export default Contact;

ルート設定

src/routes/contact.ts

/api/contact/createというパスにアクセスした際、データ保存とメール送信のコントローラーを呼び出す設定にする。

import express from "express";
import {
  createContact,
} from "../controllers/ContactController";

const router = express.Router();

router.post("/create", createContact);

export default router;

src/index.ts

アプリケーションルートで、src/routes/contact.tsのルートをセットする。

import contactRouter from "./routes/contact";

// ...省略

// ルートを設定
app.use("/api/contact", contactRouter);

これで、/api/contact/createというパスのAPIに問い合わせを送信できるようになる。

ExpressとSESの連携を実装

以下のコードを使用して、自動返信メール機能を実装する。

環境変数をセットする

プロジェクトのルートの.envファイルを作成し、上記のAWSコンソールで取得した「アクセスキーID」、「シークレットアクセスキー」、「管理者メールアドレス」、「検証済みメールアドレス」、「リージョン」をセットする。

AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_REGION=ap-northeast-1
SES_VERIFIED_EMAIL=xxx@gmail.com
ADMIN_EMAIL=xxx@gmail.com

SES設定ファイル

src/config/aws/ses.ts

このファイルは、AWS SES(Simple Email Service)を使用してメールを送信するための設定とクライアントの初期化を行う役割を担っている。

import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
import dotenv from "dotenv";

dotenv.config();

class AWSSESConfig {
  private static instance: AWSSESConfig;
  private sesClient: SESClient;

  private constructor() {
    // SESクライアントの初期化
    this.sesClient = new SESClient({
      region: "ap-northeast-1",
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
      },
    });
  }

  public static getInstance(): AWSSESConfig {
    if (!AWSSESConfig.instance) {
      AWSSESConfig.instance = new AWSSESConfig();
    }
    return AWSSESConfig.instance;
  }

  /**
   * メール送信
   * @param to 宛先
   * @param subject 件名
   * @param body string
   * @see https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v3/developer-guide/javascript_ses_code_examples.html#:r3l:-trigger
   */
  public async sendEmail(to: string, subject: string, body: string) {
    const params = {
      Destination: {
        ToAddresses: [to], // 送信先メールアドレス
      },
      Message: {
        Body: {
          Text: { Data: body }, // メール本文
        },
        Subject: { Data: subject }, // メール件名
      },
      Source: process.env.SES_VERIFIED_EMAIL!, // 検証済み送信元メールアドレス
    };

    try {
      const command = new SendEmailCommand(params);
      // メール送信実行
      const response = await this.sesClient.send(command);
      return response;
    } catch (error) {
      console.error("Error sending email: ", error);
      throw error;
    }
  }
}

export default AWSSESConfig.getInstance();
  1. AWS SESクライアントの初期化: AWS SDKを使用してSESクライアントを初期化し、必要な認証情報(アクセスキーIDとシークレットアクセスキー)を設定する。
  2. メール送信メソッドの提供: sendEmailメソッドを定義し、このメソッドを通じてメールの送信を行う。このメソッドは宛先、件名、本文を受け取り、AWS SESを使用してメールを送信する。

メール送信処理(コントローラー)の実装

src/controllers/ContactController.ts

このファイルは、問い合わせフォームからのデータを処理し、データベースに保存し、ユーザーと管理者にメール通知を行う役割を担う。

import { NextFunction, Request, Response } from "express";
import Contact from "../model/Contact";
import AWSSESConfig from "../config/aws/ses";

// 一覧取得
export const getContactList = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  try {
    const results = await Contact.find();
    res.json(results);
  } catch (error) {
    next(error);
  }
};

// 新規作成
export const createContact = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const { name, email, detail } = req.body;

  // MongoDBのトランザクションを開始
  const session = await mongoose.startSession();
  session.startTransaction();

  try {
    // 問い合わせデータの保存
    const contactModel = new Contact({ name, email, detail });
    await contactModel.save({ session });

    // ユーザーへの自動返信メール
    const userSubject = "お問い合わせありがとうございます【テック教育ナビ】";
    const userBody = `${name} 様,\n\nお問い合わせいただきありがとうございます。\n\n内容を確認し、後ほどご連絡いたします。\n\nお問い合わせ内容:\n${detail}\n\nよろしくお願いします。`;

    // 管理者への通知メール
    const adminSubject = "新しいお問い合わせ";
    const adminBody = `新しいお問い合わせがありました:\n\n名前: ${name}\nメールアドレス: ${email}\nメッセージ:\n${detail}`;

    // ユーザーへの返信メール送信
    await AWSSESConfig.sendEmail(email, userSubject, userBody);
    // 管理者への通知メール送信
    await AWSSESConfig.sendEmail(
      process.env.ADMIN_EMAIL!,
      adminSubject,
      adminBody
    );

    // トランザクションをコミットしてセッションを終了する
    await session.commitTransaction();
    session.endSession();

    res.status(201).send(contactModel);
  } catch (error) {
    // 処理に失敗した場合は、セッションを破棄してデータの保存を行わない
    await session.abortTransaction();
    session.endSession();
    next(error);
  }
};

// 物理削除
// path: /contact/delete/:id
export const deleteContact = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  try {
    const { id } = req.params;
    const result = await Contact.findByIdAndDelete(id);
    if (!result) {
      return res
        .status(404)
        .json({ message: "Delete failed!! Contact not found" });
    }
    return res.status(204).json({ message: `Deleted contact id:${id}` });
  } catch (error) {
    next(error);
  }
};

これで、メール送信機能の実装は完了。

MongoDBのトランザクション

上記の// MongoDBのトランザクションを開始などのコメントを参照。

トランザクションを使用することで、データの一貫性を保つ。
例えば今回の場合、createContact関数では以下の3つの処理を行っている。

  1. 問い合わせデータの受け取りと保存: エンドポイントに送信された問い合わせデータを受け取り、Mongooseを使用してMongoDBに保存。
  2. 自動返信メールの送信: ユーザーからの問い合わせを受け取った際に、ユーザーに対して自動返信メールを送信。
  3. 管理者への通知メールの送信: 新しい問い合わせがあったことを管理者に通知するためのメールを送信。

複数の操作を一度に行う場合はトランザクションを使用し、全ての操作が成功するか、全ての操作が失敗するようにしている。

カテゴリー: awsNode.js