Serverless Framework v1.0 で serverlessなREST APIをデプロイする方法

## 概要

[Serverless Framework](https://serverless.com) v1.0を使って、API Gateway + Lambda + DynamoDBのREST APIをデプロイします。

Serverless Frameworkは、node.jsで動作するサーバーレスアプリケーション管理ツールです。

以下の手順はnode.js及びnpmがインストールされている環境で実施します。

## 環境のセットアップ

### Serverless Frameworkのインストール

npmでserverlessをインストールします。

```Bash
npm install -g serverless
```

`serverless`コマンドを実行して、コマンド一覧が表示されればOKです。
コマンドのショートカットとして、`sls`も利用できます。

```Bash
$ serverless

Commands
...
```

### Serverless Frameworkのプロジェクト作成 

プロジェクト用のディレクトリを作成して、移動します。

```Bash
mkdir serverless-rest-example
cd serverless-rest-example
```

`serverless create`コマンドで、テンプレートを作成します。

`--template`引数でテンプレートを指定します。
ここでは、`aws-nodejs`を使います。

```Bash
sls create --template aws-nodejs
```

コマンドを実行すると、カレントディレクトリに以下のファイルが生成されます。

* serverless.yml: Serverless Frameworkの設定ファイル
* handler.js: lambdaファンクションのサンプルコード
* event.json: ローカル環境でファンクションを実行した時に渡される引数

## serverless.ymlの設定

serverless.ymlを編集して、アプリケーションを設定していきます。

### アプリケーションの基本設定

* `service:` アプリケーション名を設定します。
* `provider:` アプリケーションのデプロイ先の設定です。
  * `name:` awsとすると、AWSをデプロイ先として指定します。
  * `runtime:` lambdaのランタイムを指定します。
  * `stage:` サービスのステージ名を指定します。
  * `region:` デプロイ先のAWSリージョンを指定します。

serverless.ymlはこんな感じになります。
  
```yaml:serverless.yml
service: serverless-rest-example

provider:
  name: aws
  runtime: nodejs4.3
  stage: test
  region: ap-northeast-1
```

### lambdaファンクションの設定

`functions:`ディレクティブの下に、lambdaファンクションを追加していきます。

ここでは、`hello`という名前のlambdaファンクションを追加します。

lambdaファンクションの設定は以下のとおり:

* `handler:` lambdaのhandlerの設定です。
* `events:` lambdaのトリガーイベントの設定です。
  * `http:` APIGatewayのリクエストをlambdaのトリガーイベントに指定します。
  * `path:` REST APIのリソースパスを指定します。
  * `method:` lambdaがコールされるHTTPメソッドを指定します。

```yaml:serverless.yml
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
      - http:
          path: hello/{name}
          method: get
```

### DynamoDBテーブルの設定

DynamoDBのテーブルをCloudFormationテンプレートで定義します。

以下の例のように、`resources:`ディレクティブの下に、CloudFormationのテンプレートを書いていきます。
この例では、先ほど設定した`service:`ディレクティブの値をプリフィックスにした、usersというテーブルを作成しています。

```yaml:serverless.yml
resources:
  AWSTemplateFormatVersion: "2010-09-09"
  Resources:
    users:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:service}_users
```

## lambdaファンクションの作成

lambdaファンクションを作成します。

serverlessコマンドでデプロイしたlambdaファンクションをAPIGatewayで使用する場合、ファンクションの戻り値は以下の形式にします。

```
{
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}
```

[引用:AWS Documentation | Proxy Resourceのプロキシ統合を設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-simple-proxy-for-lambda-output-format)


serverless.ymlの中で、`GET /hello`と`GET /hello/{name}`にlambdaファンクションを紐づけました。

ここでは、`GET /hello`とすると、`Hello World!`と返し、`GET /hello/{name}`とすると、`Hello ${name}!`返すlambdaファンクションを作成します。

せっかく作ったDynamoDBのテーブルは使用していませんね。

```javascript:handler.js
module.exports.hello = (event, context, callback) => {
  const pathParameters = event.pathParameters || { name: 'World' };

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: `Hello ${pathParameters.name}!`,
    }),
  };

  callback(null, response);
};
```

## アプリケーションをデプロイする

`serverless deploy`コマンドでアプリケーションをデプロイします。
アプリケーションをデプロイするには、AWSアカウントへの認証が必要です。

手っ取り早い方法としては、環境変数に`AWS_ACCESS_KEY_ID`と`AWS_SECRET_ACCESS_KEY`を環境変数に設定する方法があります。

```Bash
export AWS_ACCESS_KEY_ID=<key>
export AWS_SECRET_ACCESS_KEY=<secret>
serverless deploy
```

すでにaws cliを使用している環境の場合、aws cliのプロファイルを使用することもできます。


デプロイしたアプリケーションを削除するには、以下のコマンドを実行します。

```Bash
serverless remove
```

## アプリケーションをローカルで実行する

`serverless invoke`コマンドで、ローカル環境でファンクションを実行できます。
`--function`で指定したファンクションを実行します。
`--path`で、実行時に`event`に渡す引数のjsonファイルのパスを指定できます。

```Bash
serverless invoke --function hello --path event.json
```

## サンプルプロジェクト

https://github.com/yurano/serverless-rest-example