目次

目次

SAM Local (Beta)を使ってローカルにAPI Gatewayを構築する

松木佑徒
松木佑徒
最終更新日2017/09/07 投稿日2017/09/07

SAM Local (Beta)が発表されローカル環境でサーバレスアプリケーションをデバッグできるようになったので試してみました。 ちなみにリンク先の記事はそのままでは動かそうとすると情報が足りなかったので、試したい人はGitHubの方のチュートリアルやサンプルを触ってみるのが良いと思います。

ローカルにLambdaを作成

チュートリアル用のHelloWorld関数をローカルに作ってみます。

template.yml

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

Description: A simple Hello World Serverless project
Resources:
  HelloWorld: # <-- 関数の論理ID
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs6.10
      Handler: index.handler

index.js

exports.handler = (event, context, callback) => {
  console.log('LOG: Name is ' + event.name);
  callback(null, "Hello " + event.name);
}

event.json

{
  "name": "Yuto"
}

実行結果

# `HelloWorld` は関数の論理ID
$ sam local invoke HelloWorld -e event.json
...(省略)...
"Hello Yuto"

HelloWorldをAPI Gatewayに対応させる

sam local start-api でAPI Gatewayが起動しますが、上の設定のまま実行するとAPIの定義が足りなくてエラーになります。

$ sam local start-api
...(省略)...
ERROR: None of the Serverless functions in your SAM template have valid API event sources.

Lambda関数の設定に Events を追加します。 Type: Api でAPI GatewayのAPIの定義になります。

...(省略)...
HelloWorld:
  Type: AWS::Serverless::Function
  Properties:
    Runtime: nodejs6.10
    Handler: index.handler
    Events:
      Api:
        Type: Api
        Properties:
          Path: /hello
          Method: get

Events の設定を追加すると Path に指定したpathにAPI Gatewayがデプロイされます。

$ sam local start-api
...(省略)...
Mounting index.handler (nodejs6.10) at http://127.0.0.1:3000/hello [GET]

APIを呼ぶとLambda関数が呼ばれるのですが以下のエラーが出ます。

$ curl http://localhost:3000/hello
...(省略)...
ERROR: Function HelloWorld returned an invalid response (must include one of: body, headers or statusCode in the response object)

API GatewayのProxy Integrationを使用しているため statusCode がないとエラーになるそうです。 レスポンスに statusCode を追加します。

exports.handler = (event, context, callback) => {
  console.log('LOG: Name is ' + event.name);
  // callback(null, "Hello " + event.name);
  callback(null, {
      statusCode: 200,
      body: "Hello " + event.name
  });
}

ここまでで実行には成功しますが event.nameundfined となってしまいます。

$ curl http://localhost:3000/hello
Hello undefined

APIにパラメータを渡す

curlで渡したデータはAPI Gatewayのイベントでラップされるので、API Gatewayのイベントをパースして使用する必要があります。

post も使いたい場合はAPIの定義を get から any に変更します。 getpost を両方マッピングする方法が見つからなかったので。 (2個定義すれば良いのかもですが今回はお試しなので簡単にanyで)

...(省略)...
Api:
  Type: Api
  Properties:
    Path: /hello
    Method: any

ソースコードもGET/POSTの両方に対応するよう修正します。

const parse_event = {
  "GET": (event) => event.queryStringParameters,
  "POST": (event) => JSON.parse(event.body)
}

exports.handler = (event, context, callback) => {
  const $event = parse_event[event.httpMethod](event);
  console.log('LOG: Name is ' + $event.name);
  callback(null, {
      statusCode: 200,
      body: "Hello " + $event.name
  });
}
$ curl http://localhost:3000/hello?name=Yuto
Hello Yuto
$ curl -d '{"name": "Yuto"}' http://localhost:3000/hello
Hello Yuto

まとめ

ホットリロードはソースの更新の度に行われるのではなくAPIの呼び出し毎にソースを読み込み直しているような動きをしているのでそこまでサクサク開発できないんじゃないかなという印象です。

サーバレスのアプリケーションをローカルでデバッグできるだけで楽といえば楽ですが、呼び出しに5秒くらいかかるので1画面で複数のAPIを呼び出すような場合は結構待たされると思います。

また、ホットリロードによりLambda関数のソース変更は再起動不要ですが、MethodをGETからPOSTに変更してみましたが反映されなかったのでAPIの定義を更新するには再起動が必要みたいです。

松木佑徒

目次