Serverlessで任意のディレクトリ配下に、関数毎にディレクトリを切ってソースを配置する with webpack building

# おーばーびゅー

AWSでAPI Gateway/Lambdaの組み合わせでREST APIを運用しています。
以前はLambdaのdeployにApexを使用していたのですが、今回API Gatewayの設定も同時に記述したく、Serverless Frameworkへの移行を決めました。
その際`serverless create --template aws-nodejs --path my-service`等のコマンドで生成されるディレクトリ構成が気に入らず...
この記事は私の望むディレクトリ構成を得るまでの軌跡です。

# 環境

macOS Sierra v10.12.6
node.js v9.2.0
yarn v1.3.2
Serverless Framework v1.25.0

# いやなところ

## `--template aws-nodejs`で生成したプロジェクト

```
my-service
├── handler.js
└── serverless.yml
```

function増やすには...？と調べてみるとエントリポイントは全部handler.jsで、中身だけ別ファイルに書いて、それをhandler.jsでexportしている実装がちらほら
そもそもエントリポイントはfunction別にあったほうがよくない？
functionを増やすとトップレベルに沢山ファイルが出来そうだし...

## [babel-dynamically-entries](https://github.com/serverless-heaven/serverless-webpack/tree/master/examples/babel-dynamically-entries)のプロジェクト例

```
babel-dynamically-entries
├── event.json
├── first.js
├── package.json
├── second.js
├── serverless.yml
├── webpack.config.js
└── yarn.lock
```

function分けられてるけど結局トップレベルかーい！

# こうしたい

```
api
├── functions
│   └── hello
│       └── index.js
├── package.json
├── serverless.yml
├── webpack.config.js
└── yarn.lock
```

こんな感じでfunctions以下に関数毎にディレクトリを切りたい

# どうやる

## init

```bash
$ serverless create --template aws-nodejs --path my-best-sls-structure
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/XXXXX/my-best-sls-structure"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.25.0
 -------'

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

$ # globalにserverlessがない場合は下記のコマンド等で適宜導入のほど
$ # npm install -g serverless
$ # yarn global add serverless --prefix /usr/local
$ cd my-best-sls-structure
$ yarn init -y
yarn init v1.3.2
warning The yes flag has been set. This will automatically answer yes to all questions which may have security implications.
success Saved package.json
✨  Done in 0.05s.
```

## install dependencies

一応serverlessも入れてます

```bash
$ yarn add babel-core babel-loader webpack serverless serverless-webpack
```

## edit files

```serverless.yml
service: my-best-sls-structure

provider:
  name: aws
  runtime: nodejs6.10

# コレが大事
# 
# ref.: https://serverless.com/framework/docs/providers/aws/guide/packaging/#packaging-functions-separately
package:
  individually: true

# webpackと連携するためにserverless-webpackを導入
plugins:
  - serverless-webpack

functions:
  hello:
    handler: functions/hello/index.handler
```

```webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');

module.exports = {
  // Serverlessのpackage.individuallyを使用する場合にはentryをserverless-webpackに任せる必要がある(slsw.lib.entries)
  // 基本的に問題は起こらないと思うが、entryが足りない！みたいなケースには下記の様なコードで対応しろとのこと(_はlodashなので適宜導入下さい)
  // entry: _.assign({myCustomEntry1: './custom/path/something.js'}, slsw.lib.entries)
  entry: slsw.lib.entries,
  target: 'node',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  }
};
```

```functions/hello/index.js
// このコードはServerless Frameworkのテンプレートに入ってるアレ
export function handler(event, context, callback) {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event
    })
  };

  callback(null, response);
}
```

# うごかす

ビルドだけ

```bash
$ serverless webpack
Serverless: Bundling with Webpack...
Time: 544ms
                   Asset     Size  Chunks             Chunk Names
functions/hello/index.js  2.98 kB       0  [emitted]  functions/hello/index
   [0] ./functions/hello/index.js 257 bytes {0} [built]
```

localで叩く

```bash
$ serverless invoke local --function hello
Serverless: Bundling with Webpack...
Time: 647ms
                   Asset     Size  Chunks             Chunk Names
functions/hello/index.js  2.98 kB       0  [emitted]  functions/hello/index
   [0] ./functions/hello/index.js 257 bytes {0} [built]
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":\"\"}"
}

```

うまくビルド出来ていますね

# まとめ

```
api
├── functions
│   └── hello
│       └── index.js
├── package.json
├── serverless.yml
├── webpack.config.js
└── yarn.lock
```

の様な、任意のディレクトリ(functions)配下で、関数毎にディレクトリを切ってソースを配置するには、下記2点が大事

- serverless.yml
    - package.individually
- webpack.config.js
    - `entry: slsw.lib.entries`

こちらからは以上です。
お疲れ様でした。