API GatewayとLambdaとSESでメールを送信するAPIを作る

Amazon Simple Email Service (Amazon SES)は、メール送受信のためのサービスです。

余談ですが、SESと聞くと(System Engineering Service)の方を連想しがち。
かくゆう私もかつてはSESでドナドナされてました(遠い目)。

さて、今回はLambdaとAPI Gatewayと組み合わせてメールを送信するAPIを作ってみます。

構成図にするとこんな感じ、シンプルですが立派なサーバレスです(ふんす!)。

SESを準備する

SESはデフォルトだとサンドボックスモードという事前に認証したメールアドレスしか送受信できないモードになっています。
サンドボックスモードのまま使うなら、送受信に使用するメールアドレスを事前に登録しておきましょう。

メールアドレスを登録すると、そのアドレスに認証リンク付きのメールが届くので、リンクを踏むと認証が完了します。

Lambda関数を準備する

Lambda関数は公式ドキュメントを参考にPythonを使って記述します。

SESを使うには、Lambdaのロールに「AmazonSESFullAccess」のポリシーを割り当てます。
import json
import logging
import boto3
from botocore.exceptions import ClientError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    
    logger.info(event)
    
    body = json.loads(event['body'])
    
    SENDER = body['sender']
    RECIPIENT = body['recipient']
    
    AWS_REGION = "us-east-1"
    
    SUBJECT = "Amazon SES Test (SDK for Python)"
    
    BODY_TEXT = ("Amazon SES Test (Python)\r\n"
                 "This email was sent with Amazon SES using the "
                 "AWS SDK for Python (Boto)."
                )
                
    BODY_HTML = """<html>
    <head></head>
    <body>
      <h1>Amazon SES Test (SDK for Python)</h1>
      <p>This email was sent with
        <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
        <a href='https://aws.amazon.com/sdk-for-python/'>
          AWS SDK for Python (Boto)</a>.</p>
    </body>
    </html>
                """
                
    CHARSET = "UTF-8"
    
    client = boto3.client('ses',region_name=AWS_REGION)
    
    # Try to send the email.
    try:
        #Provide the contents of the email.
        response = client.send_email(
            Destination={
                'ToAddresses': [
                    RECIPIENT,
                ],
            },
            Message={
                'Body': {
                    'Html': {
                        'Charset': CHARSET,
                        'Data': BODY_HTML,
                    },
                    'Text': {
                        'Charset': CHARSET,
                        'Data': BODY_TEXT,
                    },
                },
                'Subject': {
                    'Charset': CHARSET,
                    'Data': SUBJECT,
                },
            },
            Source=SENDER,
        )
    
    except ClientError as e:
        return {
            'isBase64Encoded': False,
            'statusCode': 500,
            'headers': {},
            'body': '{"message": "' + e.response['Error']['Message'] + '"}'
        }

    else:
        return {
            'isBase64Encoded': False,
            'statusCode': 200,
            'headers': {},
            'body': '{"message": "Email sent! Message ID:' + response['MessageId'] + '"}'
        }
送信元と送信先のメールアドレスをリクエストパラメータで渡せるようにしています。

API Gatewayを設定する

先ほど作ったLambda関数をAPIとしてコールできるよう、API Gatewayを設定していきます。

リソースの作成

APIとしてのリソースを定義します。
ここでは、呼び出しのパスを/ses/sendとしています。

メソッドの作成

リソースに割り当てるHTTPメソッドとメソッドによって呼び出されれるものを定義します。
「Lambda プロキシ統合の使用」をオンにするとより詳細なリクエストの内容がevent変数に格納されます。

今回は「Lambda プロキシ統合の使用」をオンにした状態のリクエストを受けられるようにLambda関数を作っているので、オンにします。

先ほど作ったLambda関数を設定します。

モデルの作成

モデルというリクエスト・レスポンスのスキーマ定義を作成できます。
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "SesSendReqest",
  "type" : "object",
  "required": ["sender", "recipient"],
  "properties" : {
    "sender" : { "type" : "string" },
    "recipient" : { "type" : "string" }
  }
}

モデルを作成して、メソッドに割り当て、リクエストの検証を設定することで、パラメータが不正の場合400エラーを返してくれるようになります。

Lambda関数側でのリクエストパラメータのバリデーションチェックを簡略化することができます。

APIのデプロイ

設定が完了したらAPIをデプロイします。
API Gatewayは開発、本番など環境を分けられるよう、ステージという単位でデプロイできるようになっています。

ステージを作成すると、エンドポイントとなるURLが得られるのでAPIにリクエストを行うことができます。

APIにリクエストを行う

それではPostmanを使ってリクエストを行ってみます。

リクエストが成功し、メールも届きました。


通常なら、こういったことがやりたければメールサーバーとAPIサーバーを建てなければいけないところが、AWSのサーバーレスアーキテクチャを使えば、Lambda関数を記述して設定を行うだけで済んでしまいました。