Node.js
AWS
serverless

Serverless Framework 1.0 で気づいた事まとめ

More than 1 year has passed since last update.

Serverless Framework が0の時代にいくつか触ってたんだけど、1になって全然変わっちゃったのでしばらく遠ざかってました。
久しぶりに触ってみたら、0系からの簡易なマイグレーションは難しそうけど、過去のチラ見しつつ作り直しても手間じゃないんじゃないか、と思うほど設定が簡単になっていたので、いくつか自分のやりたいことを中心に気づいたことまとめ。

プロジェクト(サービス?)作成

以前のServerlessにあった"project"という概念はなくなっているので、プロジェクトと表現するのが適切かはよくわからないけど...
後で書くように設定ファイルの中では、"service"という言葉が使われているので、それを使うべきなのかもしれない。

$ sls create --template aws-nodejs --path slstest
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/user/github/slstest"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.3.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"

こんな感じで実行してやると、以下の構成が作られる。

$ tree slstest
slstest
├── handler.js
└── serverless.yml

0 directories, 2 files

シンプルすぎるやろ! 必死で覚えた以前の複雑さどこに消えた!
serverless.ymlの中身はこんな感じ(コメントアウト削除)。

serverless.yml
service: slstest
provider:
  name: aws
  runtime: nodejs4.3
functions:
  hello:
    handler: handler.hello

handler.jsの中身はこう。

handler.js
'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);
};

この状態で、

$ sls deploy

を実行すると、lambdaに "slstest-dev-hello"ファンクションが作られます。
ファンクションの中身は、handler.jsの中の、hello関数が定義されます。

が、これだけだとWebから叩いて実行はできません。
これだけだとlambdaしか定義されておらず、API Gatewayが定義されていないからです。
API Gatewayを定義するには、functionの中のevents設定を使います。

serverless.yml書き換え(抜粋)
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

これで"sls deploy"を行うと、API Gatewayにdev-dldtest APIが作成され、その下にリソース/helloのメソッドGETが作成され、それが"slstest-dev-hello"ファンクションに紐づけられます。

APIを増やす

単純に、jsファイルを増やしてやって、serverless.ymlでそれのlambdaやAPI Gatewayへの紐づけ方を定義してやればOK。
例えば、handler.jsをコピペしたhamdler2.jsを作ってやって、それをGET /hello2に紐づけたいなら、

serverless.yml書き換え(抜粋)
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
  hello2:
    handler: handler2.hello
    events:
      - http:
          path: hello2
          method: get

で動きます。

クエリストリングの取得

Serverless 0系の場合はあれほどs-template.ymlあたりをハックしてやらないとクエリストリングを取るのすら苦労してたのが嘘のようなのですが、Serverless 1系では普通にクエリストリングがパースされてくるようです。

テンプレートから作られたhandler.jsの記述を見ると、

handler.js(抜粋)
module.exports.hello = (event, context, callback) => {
    ...
};

この"event"の、event.input.queryStringParametersからパースされた値が取れるようです。

PathInfoの取得

さすがにPathInfoの取得は、API Gatewayに対応したリソースの追加がいるので指定しておかないと読み込めません。

設定は、serverless.ymlで、以下のような感じ。

serverless.yml書き換え(抜粋)
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello/{x}/{y}
          method: get
          integration: lambda
          request:
            parameters:
              paths:
                x: true
                y: true  

これで、PathInfoの情報が分解されて、event.input.pathにオブジェクトとして入ってきます。
キモというか、気をつけないといけないのは、"integration: lambda"という一節です。

lambda-proxy integrationとlambda integration

これは、API Gatewayとlambdaの間の紐付け時の中間処理(integration)を、"lambda-proxy"形式ではなく"lambda"形式で行うという宣言です。
両者の違いは、"lambda-proxy"形式がhttpに特有な処理をより「よしなに」やってくれる感じで、"lambda"形式がより生に近いものを操作できる感じです。

Pathinfoを読むためには"lambda"形式を指定しないといけないのですが、その副作用?として、まず入力で"lambda-proxy"形式だとevent.input.queryStringParametersに入ってくるクエリストリングが、"lambda"形式だとevent.input.queryに入ってきます。
また、自動生成されるhandler.jsに、

handler.js(抜粋)
  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });

と書かれているように、"lambda"形式だと、callbackの引数にはstringではなくobjectを返す必要があるようです。

追加モジュールなどのアップロード

これは、serviceのフォルダ内にサブフォルダ(node_modules以外も含め)などを設定しておくと、一緒にパッケージングして送ってくれるようで、フォルダ外のものを選択的に追加したり、内のものを選択的に除外したりは動かないようです。
テンプレート生成のserverless.ymlに、

serverless.yml(抜粋)
# you can add packaging information here
#package:
#  include:
#    - include-me.js
#    - include-me-dir/**
#  exclude:
#    - exclude-me.js
#    - exclude-me-dir/**

とあるので、この辺をいじってやれば制御できるのかなと思いましたが、特に何も変わりませんでした。
将来的に導入されるのかもしれません。

駆け足でとりとめもなかったですが、Serverless 1系を触ってみて、触ってみた範囲で0系と違うところのまとめでした。