🦔

Serverless Framework で API Gateway を利用する

2021/04/11に公開

Serverless Framework で API Gateway と Lambda を使った、サーバーレスな API の作成方法について説明します。

前提

Serverless Framework がインストール済みで、AWS のアカウント設定済みの前提で進めます。
Serverless Framework のインストールがまだの場合は、こちらの記事でインストール方法などは記載していますので、そちらを確認してください。

API Gateway と接続した Lambda の設定

Serverless Framework で Lambda を起動するイベントを設定する場合は、serverless.yml のfunctionseventsを追加します。
このevents部分には Lambda を起動するイベントとして、S3 の put イベントや DynamoDB Stream、CloudWatch Events など様々なイベントを設定できます。

serverless.yml の設定

今回は API Gateway をイベントとして使いたいので、以下のように設定します。

serverless.yml
functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:
          path: /hello
          method: get

httpApi部分が API Gateway をイベントとして設定するという意味になります。
API Gateway では、HTTP API と REST API の 2 種類が選択できます(詳細はAWS のドキュメントを参照してください)。
HTTP API を利用したい場合はhttpApi、REST API を利用したい場合はhttpで設定してください。どちらを使うか悩む場合は、基本的に HTTP API の利用で問題ないです。

pathはパスパラメータ、methodは HTTP メソッドです。
今回は/hello というパスで、GETメソッドで取得できる API を作成するという意味になります。

Lambda のコード内容

Lambda のコードはsls create -t aws-python3で作成したテンプレートのままですが、以下になります。

handler.py
import json

def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

API Gateway と連携する場合、レスポンスをstatusCodebodyを key とした JSON 形式で返す必要があります。

statusCodeは HTTP ステータスコードのことで、正常でのレスポンスなら200になります。
bodyは実際に返す JSON データです。

AWS にデプロイ

上記の内容を AWS にデプロイします。

$ sls deploy
〜省略〜
endpoints:
  GET - https://<yourapigatewayid>.execute-api.ap-northeast-1.amazonaws.com/hello
〜省略〜

デプロイを実行すると、最後の方にendpointsというものが出力されます。これが作成された API Gateway の URL になります。

この URL に対して API を実行してみます。JSON 整形のために jq コマンドを使用していますが、これは見やすくしてるだけなので、なくても問題ありません。
<yourapigatewayid>となっている部分は、適時自分の URL で読み替えてください。

$ curl -XGET 'https://<yourapigatewayid>.execute-api.ap-northeast-1.amazonaws.com/hello' | jq .
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": {/*色んなデータ*/}
}

コード上のbodyの中身がレスポンスとして出力されています。
message部分はコードに書いたままの文字列が、input部分はeventをそのまま渡しているので、API Gateway 特有のイベントデータがすべて出力されています。

また、クエリ文字列を渡すと、eventの中にqueryStringParametersというパラメータが含まれるので、その値を使うことで GET パラメータを使用できます。

クエリ文字列の利用方法

API のデプロイまではできたので、API でクエリ文字列を利用する方法について説明します。
クエリ文字列を確認するために、コードをちょっと変更します。

import json

def hello(event, context):
    params = event.get('queryStringParameters') # パラメータ取得

    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": params # パラメータのみ表示に変更
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

コードの変更を反映するために、再度デプロイします。

$ sls deploy

クエリ文字列の確認

デプロイできたら、API を再実行します。
まずは、クエリなしで実行。

$ curl -XGET 'https://<yourapigatewayid>.execute-api.ap-northeast-1.amazonaws.com/hello' | jq .
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": null
}

クエリなしなので、input の値が null で返ってきています。
次に、クエリありで実行します。param1=123&param2=abcというクエリを設定してリクエストします。

$ curl -XGET 'https://<yourapigatewayid>.execute-api.ap-northeast-1.amazonaws.com/hello?param1=123&param2=abc' | jq .
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": {
    "param1": "123",
    "param2": "abc"
  }
}

inputの中にkey: valueの形式で値が入ってきました。
そのため、eventqueryStringParametersは以下のような形で入っていることがわかります。また、クエリ文字列なので、value がすべて string 型になっている点は注意してください。

{
  "queryStringParameters": {
    "param1": "123",
    "param2": "abc"
  }
}

API Gateway を利用する際の注意点

API Gateway を利用する場合、Lambda のタイムアウト設定に注意してください。
現時点の仕様だと Lambda のタイムアウトは最大 15 分まで設定可能ですが、API Gateway のタイムアウトは最大 30 秒になっています。HTTP API は 30 秒固定、REST API は最大 29 秒で設定可能です。詳細はAWS のマニュアルを確認してください。

そのため、Lambda のタイムアウト値は API Gateway のタイムアウト以下に設定してください。
Lambda のタイムアウト値が API Gateway のタイムアウトより長い場合、API のレスポンスはタイムアウトなのに Lambda 自体は正常終了することがあるためです。
Lambda での処理が長時間かかるような場合、API のレスポンスは即時で返して、それとは別に非同期で結果を連携する仕組みなどを考える必要があります。

おまけ

今回使用したコードは以下の Github リポジトリにアップしているので、全体のコードを確認したい方はそちらを見てください(v2.0 のタグが今回のコードです)。
https://github.com/ombran/serverless-sample/tree/v2.0

Discussion