LoginSignup
38
25

More than 5 years have passed since last update.

SAM + SwaggerでAPIGateway + Lambdaを構築

Posted at

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"

38
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
25