【初心者向け】SwaggerとAWS SAMを使ってWebAPIを簡単に作ってみた

はじめに

サーバーレス開発部の藤井元貴です。インフルエンザに怯えてます。(予防接種はしてる)

API Gatewayでは、WebAPIの作成にSwaggerを使用できます。 Swaggerを使うことで、ドキュメント内容とWebAPI仕様の一致が期待できます(齟齬がない)。

私にとってSwaggerは初めて使うため、ひとまず下記を試してみました。

  • Swaggerを使ってWebAPIを作る
  • AWS SAMを使う

まずはシンプルに試すため、最低限の内容です。 例えば、開発環境と本番環境(devとprod)のように分けていません。

おすすめ

  • SwaggerでAPI Gatewayを定義したい人
  • AWS SAMを使いたい人
  • サーバーレスなWebAPIに興味がある人

環境

項目 バージョン
macOS High Sierra 10.13.6
AWS CLI aws-cli/1.16.89 Python/3.6.1 Darwin/17.7.0 botocore/1.12.79
AWS SAM 0.10.0
Docker for Mac 18.09.1
Python 3.6

全体概要

WebAPIを作成します。裏側にはLambdaを配置するサーバーレスな構成です。

API Gatewayの定義にSwaggerを使用し、デプロイにAWS SAMを使用します。

WebAPIの仕様

Swaggerを使う前に、まずはWebAPIの仕様を考えます。

Path Method 概要
/version GET LambdaのPythonバージョンを取得する

応答パラメータの例は下記とします。

{
  "python": "3.6.2"
}

AWS SAMプロジェクトの準備

下記コマンドでプロジェクト一式を作成します。

sam init --runtime python3.6 --name SwaggerSample

Swagger Editor

導入

こちらを参考に導入します。Dockerは便利ですね。

API定義

Swagger Editorで下記を作成しました。この内容をswagger.yamlとして、AWS SAMプロジェクトフォルダに保存します。

swagger: "2.0"
info:
  description: "SwaggerとAPI Gatewayのサンプルです。"
  version: "1.0.0"
  title: "Swagger Sample"
basePath: "/Prod"
tags:
  - name: "Version"
schemes:
  - "https"
paths:
  /version:
    get:
      tags:
        - "Version"
      summary: "Pythonバージョン取得"
      description: "Lambdaで動いているPythonのバージョンを取得します。"
      consumes:
        - "application/json"
      produces:
        - "application/json"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/Version"
      x-amazon-apigateway-integration:
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PythonVersionFunction.Arn}/invocations
        passthroughBehavior: when_no_templates
        httpMethod: POST
        type: aws_proxy
definitions:
  Version:
    type: "object"
    required:
      - "python"
    properties:
      python:
        type: "string"

API Gatewayで必要となるx-amazon-apigateway-integrationを記載しています。詳細はこちらへ。

なお、下記のようなドキュメントになります。参考まで。

AWS SAM

Lambda関数

Lambda関数のコードは下記です。Pythonバージョンの文字列を返却します。

import sys
import json


def lambda_handler(event, context):
    version = f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}'

    return {
        "statusCode": 200,
        "body": json.dumps(
            {"python": version},
        ),
    }

templateファイル

AWS SAMのtemplate.yamlは下記です。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
    SwaggerSample

    Sample SAM Template for SwaggerSample

Globals:
    Function:
        Timeout: 3

Resources:
    PythonVersionApi:
        Type: AWS::Serverless::Api
        Properties:
            StageName: Prod
            DefinitionBody:
                Fn::Transform:
                    Name: AWS::Include
                    Parameters:
                        Location: s3://cm-fujii.genki-sam-test-bucket/swagger.yaml

    PythonVersionFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello_world/
            Handler: app.lambda_handler
            Runtime: python3.6
            Events:
                HelloWorld:
                    Type: Api
                    Properties:
                        Path: /version
                        Method: get
                        RestApiId: !Ref PythonVersionApi

Outputs:
  PythonVersionApiUrl:
      Description: "API Gateway endpoint URL for Prod stage for Python Version Function"
      Value: !Sub "https://${PythonVersionApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/version"

Type: AWS::Serverless::ApiでAPIを明示的に定義し、S3に格納してあるyamlファイル(swagger.yaml)を指定します。

S3

コード等を格納するためのS3バケットを作成します。作成済みの場合は飛ばします。

aws s3 mb s3://cm-fujii.genki-sam-test-bucket

Swaggerファイルを格納

SwaggerファイルをS3バケットに格納します。

aws s3 cp swagger.yaml s3://cm-fujii.genki-sam-test-bucket/swagger.yaml

ビルド

下記コマンドでビルドします。

sam build

動作確認(ローカル)

まずはAPIを準備します。

sam local start-api

続いてcurlでAPIを叩きます。

$ curl http://localhost:3000/version
{"python": "3.6.1"}

OKですね!

package

続いてコード一式をS3バケットにアップロードします。

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-sam-test-bucket

deploy

最後にデプロイします。

sam deploy \
    --template-file packaged.yaml \
    --stack-name SwaggerSample \
    --capabilities CAPABILITY_IAM

動作確認

作成したWebAPIのエンドポイントを確認します。

Web画面ポチポチでも良いですが、せっかくなのでコマンドを使います。

$ aws cloudformation describe-stacks --stack-name SwaggerSample --query 'Stacks[].Outputs'
[
    [
        {
            "OutputKey": "PythonVersionApiUrl",
            "OutputValue": "https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/version",
            "Description": "API Gateway endpoint URL for Prod stage for Python Version Function"
        }
    ]
]

では、作成したWebAPIを叩いてみましょう!

$ curl https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/version
{"python": "3.6.8"}

Python3系で最新の3.6.8が使用されていました。

ハマったこと

Swaggerの記載で下記にハマりました。

  • definitionsexampleを記載すると、sam deployが失敗する
  • WebAPIのMethodがGETでも、x-amazon-apigateway-integrationhttpMethodはPOSTを指定する

どちらもAWS(AWS SAM、API Gateway、Labmda)との連携部分です。辛かった……。

Swagger Editorですべて頑張らずに、AWSのWeb画面でAPI Gatewayをポチポチ作成したあと、Swagger形式でエクスポートするのも有効です。むしろこのほうが効率良さそう。

さいごに

SwaggerとAWSの組み合わせは、使い倒すほどハマる点が増えそうに感じましたが、Infrastructure as Codeは良いですね!! 楽しいです!

参考