thumbnail

serverlessフレームワークでmail用APIを用意する

2022/06/09

serverlessはサーバーレスなアーキテクチャをかんたんに作成できるオープンソースのフレームワークです。 AWS Lambdaだけでなく、Azure Functions、Google CloudFunctionsなどに対応しています。

Set Up

今回はlabmdaを使用する前提ですすめます。 serverlessフレームワークはnpmでインストール可能です。

shell
$ npm install -g serverless

以下のコマンドでawsのlabmdaでnodeを動かすtemplateが作成されます。

shell
$ serverless create --template aws-nodejs

このテンプレートでは以下のファイルが生成されます。

handler.js (Clickで詳細)
handler.js
'use strict';

module.exports.hello = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Go Serverless v1.0! Your function executed successfully!',
        input: event,
      },
      null,
      2
    ),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
serverless.yml (Clickで詳細)
serverless.yml
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: example
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iamRoleStatements:
#    - Effect: "Allow"
#      Action:
#        - "s3:ListBucket"
#      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#    - Effect: "Allow"
#      Action:
#        - "s3:PutObject"
#      Resource:
#        Fn::Join:
#          - ""
#          - - "arn:aws:s3:::"
#            - "Ref" : "ServerlessDeploymentBucket"
#            - "/*"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  include:
#    - include-me.js
#    - include-me-dir/**
#  exclude:
#    - exclude-me.js
#    - exclude-me-dir/**

functions:
  hello:
    handler: handler.hello
#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - http:
#          path: users/create
#          method: get
#      - websocket: $connect
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
#      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
#      - iot:
#          sql: "SELECT * FROM 'some_topic'"
#      - cloudwatchEvent:
#          event:
#            source:
#              - "aws.ec2"
#            detail-type:
#              - "EC2 Instance State-change Notification"
#            detail:
#              state:
#                - pending
#      - cloudwatchLog: '/aws/lambda/hello'
#      - cognitoUserPool:
#          pool: MyUserPool
#          trigger: PreSignUp
#      - alb:
#          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
#          priority: 1
#          conditions:
#            host: example.com
#            path: /hello

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"

テンプレートを作成できたらローカルですぐに動作確認ができます。

shell
$ sls invoke local -f hello
# { message: 'Go Serverless v1.0! Your function executed successfully!', event };

localを外すとdeploy先のlabmdaを叩きます。

Mailのhandlerを用意

handler.jsのようなMail用のhandlerを用意します。

mailHander.js
const dayjs = require('dayjs');
dayjs.extend(require('dayjs/plugin/timezone'));
dayjs.extend(require('dayjs/plugin/utc'));
dayjs.tz.setDefault('Asia/Tokyo');

const nodemailer = require('nodemailer');
const { MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASS, MAIL_DEST } = process.env;

const transport = nodemailer.createTransport({
  host: MAIL_HOST,
  port: MAIL_PORT,
  secure: true,
  auth: {
    user: MAIL_USER,
    pass: MAIL_PASS,
  },
});

module.exports.sendEmail = async (event) => {
  const body = JSON.parse(event.body);
  const now = dayjs.tz().format('YYYY/MM/DD HH:mm');

  const text = `HPよりお問い合わせを受信しました。

+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-+:-+:-+:-+:-+

受付日時:${now}

■お問い合わせ情報

お問い合わせ内容:
${body.content}

■お客様情報

会社名    : ${body.company}
お名前    : ${body.name}
メールアドレス: ${body.mail}
お電話番号  : ${body.phone}

+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-:+:-+:-+:-+:-+:-+
  `;

  let info = await transport.sendMail({
    from: `"お問い合わせフォーム" <${MAIL_USER}>`,
    to: MAIL_DEST,
    subject: 'お問い合わせ',
    text,
  });

  return {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true,
    },
    body: JSON.stringify(
      {
        message: 'send e-mail',
      },
    ),
  };
};

serverless.ymlのfunctionsに上のemailHandlerを追記します。

serverless.yml
functions:
  sendEmail:
    handler:
     emailHandler.sendEmail
		# APIとして使用したいので、以下も記入
		events:
      - http:
          path: email
          method: post
  hello:
    handler: handler.hello

events: -httpを追記することでCloud FrontとAPI Gatewayをよしなに設定してくれます。

Lambdaへのdeploy

事前にIAMでcredentialsの設定(基本的にはaccess keyとsecret keyを登録するだけ)を行ったあと https://dev.classmethod.jp/articles/easy-deploy-of-lambda-with-serverless-framework/

以下のコマンドを実行するだけでdeployできます。

shell
$ sls deploy

内部的には、CloudFormationのスタックを作成し、それに基づいてIAMやLambda等の設定を行っているっぽいです。 (Cloud Front、Cloud Watch、その他のサービスとの連携を自動でやってくれるので便利)

デプロイが完了すると以下のようなログが出ます。

Service Information
service: utility-mail-api
stage: prod
region: ap-northeast-1
stack: utility-mail-api-prod
resources: 14
api keys:
  None
endpoints:
  POST - https://example.execute-api.us-east-1.amazonaws.com/prod/email
functions:
  sendEmail: utility-mail-api-prod-sendEmail
  hello: utility-mail-api-prod-hello
layers:
  None

ちなみにdeployを取り消したいときは以下のコマンドでOKです。

shell
$ sls remove

CloudFormationを使っている関係上、aws consoleからマニュアルでdeployを削除したりするとめんどくさいことになる可能性があるので基本的にはコマンドからの削除を推奨します 😅

おわり

以下の要な場合にはserverlessはオススメ

  • 複雑なAPIは必要ない
  • サーバの管理をしたくない
  • お金をかけたくない
author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi