Serverless FrameworkでAWSにAPIバックエンドを作る時のベストプラクティス(をまとめたい)

「このパラメーターつけておいたほうがいいよ」とか
「このプラグイン入れておくとめっちゃ便利」みたいなのを含め、そろそろベストプラクティス的なものがまとまらないかなぁと思い、とりあえず作ってみました。

自分が思う「これやっとけリスト」を追加してありますが、「いやこれも必要だろ」みたいなのがあれば編集リクエストで教えてもらえると助かります。

## ステージによって設定を変更する
APIを作る時、だいたい作業中・devテスト用・本番の3種類のスタックが立ち上がることになります。
そして本番以外のスタックにはLambdaのメモリサイズをそんなに使いたくないことや、使用するAPIキーを変更したいということがあります。

そういった場合にcustomを使うことで毎回設定を書き換えずとも`sls deploy --stage XXX`で指定するだけで動的に設定を変えれるようにできます。

```
custom:
  stage: ${opt:stage, self:provider.stage}
  apiSecret:
    development: "api-secret-development~true"
    production: "api-secret-production~true"
    default: "api-secret-development~true"
  memorySize:
    development: 128   # stage[development]は256M
    production: 512    # stage[production]は512M
    default: 128       # stageが見つからなかったらこれにfallbackするために設定

provider:
  name: aws
  runtime: nodejs8.10
  timeout: 30
  stage: development
  memorySize: ${self:custom.memorySize.${self:custom.stage}, self:custom.memorySize.default}
  environment:
    API_SECRET: ${ssm:${self:custom.apiSecret.${self:custom.stage}. self:custom.apiSecret.default}}
```

## Deployment Bucketを指定する

Serverless FrameworkはCloudFormationを利用してリソースをAWSへアップします。
そしてデフォルト設定だと、スタック毎にデプロイに使用するS3バケットが生成されます。

複数人で開発をしたり、複数のAPIを作成したりしているといつのまにかS3のバケット制限に引っかかるということが考えられます。（というか1回ひっかかりました）
なのでアカウントにデプロイ用のバケットを１つ作成し、それを利用するように設定しておきましょう。

```
provider:
  name: aws
  runtime: nodejs8.10
  timeout: 30
  stage: development
  deploymentBucket: serverless.${self:provider.region}.deploys
```

## CloudWatch Logsのログ保存期間を指定する
デフォルトでは30日ログが保持されます。参考：https://qiita.com/horike37/items/86d1626b3ee1a2e86b9a#logretentionindays-config
ですが本番以外のステージでは30日も保存する必要ないことが少なくありません。
ですのでこちらもステージ別に保持期限を設定しておくと良いでしょう。

```
custom:
  stage: ${opt:stage, self:provider.stage}
  logRetentionInDays:
    development: 14
    production: 90
    default: 3

provider:
  name: aws
  runtime: nodejs8.10
  timeout: 30
  stage: development
  logRetentionInDays: ${self:custom.logRetentionInDays.${self:custom.stage}, self:custom.logRetentionInDays.default}
```

## API tokenなどはSystems ManagerのParameter Storeに

LambdaからStripeやIntercomなどの外部サービスAPIにリクエストを出したい時があります。
これまではdirenvなどで環境変数にsecret tokenを保存し、その値をenvironmentに入れてデプロイするというパターンがありました。
この場合、開発に参加するエンジニアの全環境に正しく環境変数の値が設定されている必要があり、Circle CIやTravis CI経由でのデプロイではトピックブランチのテストデプロイがやりづらいという問題がありました。
そしてなによりsecret tokenがいろんな人のPCに保存された状態というのはあまり健全ではないでしょう。

しかし今ではSystems ManagerのParameter Storeを使うことで、エンジニアはこれらのtoken情報などを意識する必要がなくなりました。

```
provider:
  name: aws
  runtime: nodejs8.10
  timeout: 30
  stage: development
  memorySize: ${self:custom.memorySize.${self:custom.stage}, self:custom.memorySize.default}
  environment:
    NON_SECRET: ${ssm:secret-development}
    API_SECRET: ${ssm:secret-development~true}
```

`SecureString`で保存している場合は`~true`を末尾につけてやればOKです。

## prune-pluginは入れておこう

デプロイを実行すると、新しいソースファイルがAWSにアップされます。そしてデフォルトでは古いものはそのまま残ります。
デプロイ回数が増えてくると、この古いファイルが容量を圧迫してきますので、プラグインを入れて削除することをオススメします。

https://github.com/claygregory/serverless-prune-plugin

```
$ npm i -D serverless-prune-plugin
or
$ yarn add -D serverless-prune-plugin
```

```
service: awesome-api

plugins:
  - serverless-prune-plugin

custom:
  prune:
    automatic: true
    number: 3
```

## そのほか随時追加予定（または編集リクエスト募集）

「ひさびさにServerless Framework使うかー」となった時に設定漏れをしないように
これからServerless Frameworkをはじめようという人が既出の問題で悩まないように

どこかにこういう情報がまとまっているといいなと思います。
なので「せやな」「確かに」と思われた方で、「あれ、これ抜けてない？」とか「こここのほうがええよ」というのがあればぜひ編集リクエストをお願いしますm(_ _)m

## 追記
Serverless Frameworkのコア開発に参加されている堀家さんから以下のコメントをいただきました。

> 追加するとしたら、serverless-alias-pluginを使って、v1とv2のバージョニング戦略を実施する。ドメインに、domain-managerプラグイン使う。CFNのリソース200個制限に引っかかりそうになったら、Share API Gatewayの機能使うとかかなー。
https://twitter.com/horike37/status/1073080097156911105

この２つの様子なので、触り次第追記します。
https://www.npmjs.com/package/serverless-aws-alias
https://github.com/amplify-education/serverless-domain-manager