Help us understand the problem. What is going on with this article?

SAM + SwaggerでAPIGateway + Lambdaを構築

More than 1 year has passed since last update.

aws-sam-cliとSwaggerを組み合わせてAPIGateway+Lambdaのサーバレス構成を構築する際にいろんな罠にハマり、CloudFormationに振り回されたので、この記事では最低限の構成を構築するために必要なテンプレートの記述を紹介たいと思います。

前提

当記事では以下のAPIGateway + Lambdaを構築していきます。

APIGateway

  • リソース名:FugaApi
  • エンドポイント
    • /hoge
      • GET
      • OPTIONS

Lambda

  • HogeFunction
    • /hogeで呼び出されるLambda

これからこの構成を一つのyamlファイルでテンプレートを用意していきます。

LamdbaRole

まずはLambdaにつけるロールを用意します。
ManagedPolicyArnsでAWSが用意しているポリシーをつけられるのは便利ですね。

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: HogeLambdaRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess

Lambda

Lambda本体を定義します。
Roleには先程定義したLambdaRoleを指定しています。
EventsにはAPIGatewayを指定するようにしています。
RestApiIdに後述するAPIGatewayのリソースFugaApiを指定しています。
RestApiIdをつけ忘れるとFugaApiとは別のAPIGatewayが作られてしますので注意してください。
(というかSwaggerで定義した場合はEventブロックそのものが要らない可能性もあります。
確認出来次第修正します。)

  HogeFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: HogeFunction/
      Role: !GetAtt LambdaRole.Arn
      FunctionName: HogeFunction
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref FugaApi
            Path: /hoge
            Method: GET

Globalセクション

実は上記のLambdaテンプレートにはHandlerRuntimeなどのプロパティが不足しています。
今回はそのあたりのプロパティはGlobalセクションに記述しています。
SAMテンプレートではGlobalセクションでテンプレート内のリソースの共有設定を記述できます。
今回の構成ではあまり意味がありませんが、Lambdaを複数定義したい場合などに共通する設定は
Globalセクションに記述できるのでテンプレートの記述が非常に楽になります。

また、各リソースのテンプレートで個別に定義したいプロパティを記述すれば上書きしてくれます。
SAMを利用する場合は是非活用しましょう。

Globals:
  Function:
    Timeout: 30
    Handler: app.lambdaHandler
    Runtime: nodejs8.10
    Environment:
      Variables:
        TZ: Asia/Tokyo

Permission

APIGatewayからLambdaにアクセスできるようにPermissonを定義しましょう。
Permissonを定義し忘れても構築自体はできますがAPIGatewayからLambdaにアクセスできないので、
APIを叩いても必ず500エラーが返り、Lambdaも実行自体されないのでログもでないという状態になります。
私はそれで小一時間悩みました。。

  HogeFunctionPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref HogeFunction
      Principal: apigateway.amazonaws.com

APIGateway

APIGatewayはSwagger形式でエンドポイントを定義できます。
DefinitionBodyにSwaggerを記述していきましょう。
今回はCORSを利用できるようにするためoptionsメソッドも記述しています。

注意点としてはoptions.responsesのステータスコードは数値でなく文字列で定義してください。
今回のようなSAMテンプレートに直接Swaggerを定義した構成の場合、数値のままだとCloudFormationで失敗します。

  FugaApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: FugaApi
      StageName: api
      DefinitionBody:
        swagger: "2.0"
        schemes:
          - "https"
        paths:
          /hoge:
            get:
              responses: {}
              x-amazon-apigateway-integration:
                uri: !Join [ '' , [ 'arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/' , !GetAtt  HogeFunction.Arn , '/invocations' ] ]
                passthroughBehavior: "when_no_match"
                httpMethod: "POST"
                type: "aws_proxy"
            options:
              consumes:
              - "application/json"
              produces:
              - "application/json"
              responses:
                "200": # ここは文字列で定義する
                  description: "200 response"
                  schema:
                    $ref: "#/definitions/Empty"
                  headers:
                    Access-Control-Allow-Origin:
                      type: "string"
                    Access-Control-Allow-Methods:
                      type: "string"
                    Access-Control-Allow-Headers:
                      type: "string"
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: "200"
                    responseParameters:
                      method.response.header.Access-Control-Allow-Methods: "'*'"
                      method.response.header.Access-Control-Allow-Headers: "'*'"
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                requestTemplates:
                  application/json: "{\"statusCode\": 200}"
                passthroughBehavior: "when_no_match"
                type: "mock"

まとめ

最終的に以下のようなテンプレートファイルになります。

特に気をつけたい点、というか私がハマった点は以下になります。

  • LambdaにAPIGatewayのPermmison設定を忘れずに
  • RestApiIdの定義も忘れずに
  • Swaggerでresponseを定義する場合は文字列で定義すること

みなさんは是非CloudFormationに振り回されないようなAWSライフをお過ごしください

samtemplate.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
   pokemon web api template

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 30
    Handler: app.lambdaHandler
    Runtime: nodejs8.10
    Environment:
      Variables:
        TZ: Asia/Tokyo

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: HogeLambdaRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
  LambdaRoleProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
          - !Ref LambdaRole

  HogeFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: HogeFunction/
      Role: !GetAtt LambdaRole.Arn
      FunctionName: HogeFunction
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref FugaApi
            Path: /hoge
            Method: GET
  HogeFunctionPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref HogeFunction
      Principal: apigateway.amazonaws.com


  FugaApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: FugaApi
      StageName: api
      DefinitionBody:
        swagger: "2.0"
        schemes:
          - "https"
        paths:
          /hoge:
            get:
              responses: {}
              x-amazon-apigateway-integration:
                uri: !Join [ '' , [ 'arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/' , !GetAtt  HogeFunction.Arn , '/invocations' ] ]
                passthroughBehavior: "when_no_match"
                httpMethod: "POST"
                type: "aws_proxy"
            options:
              consumes:
              - "application/json"
              produces:
              - "application/json"
              responses:
                "200":
                  description: "200 response"
                  schema:
                    $ref: "#/definitions/Empty"
                  headers:
                    Access-Control-Allow-Origin:
                      type: "string"
                    Access-Control-Allow-Methods:
                      type: "string"
                    Access-Control-Allow-Headers:
                      type: "string"
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: "200"
                    responseParameters:
                      method.response.header.Access-Control-Allow-Methods: "'*'"
                      method.response.header.Access-Control-Allow-Headers: "'*'"
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                requestTemplates:
                  application/json: "{\"statusCode\": 200}"
                passthroughBehavior: "when_no_match"
                type: "mock"

meno-m
opst
情報技術と社員力でお客様を成功に導く Make IT Your Success
https://www.opst.co.jp/
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした