Nuxt.js(v2.2.0)+TypeScriptなアプリをAWS Lambda+αにデプロイしてみた(S3なし版)

前回、以下のリポジトリからカスタムドメインの利用を外した構成で、Nuxt.js(v2.2.0)+TypeScriptのアプリがAPI Gateway+AWS Lambdaにデプロイできることを確認しました。

Nuxt.js(v2.2.0)+TypeScriptなアプリをAWS Lambda+αにデプロイしてみた
https://qiita.com/kai_kou/items/5b1ebb9abc5d62545e04

jeehyukwon/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/jeehyukwon/nuxt-serverless


今回は、以下のような記事を発見してしまったので、S3を外した構成を試してみます。発見したからには仕方がないです。やってみます。

Serverless-Side Rendering With AWS Lambda & NuxtJS
https://medium.com/@fernalvarez/serverless-side-rendering-with-aws-lambda-nuxtjs-b94d15782af5


## 手順

### 環境構築

GitHubに今回のソースをアップしていますので、よければご参考ください。
https://github.com/kai-kou/nuxt-serverless/tree/feature/no-use-s3


前回フォークしたリポジトリを利用します。
kai-kou/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/kai-kou/nuxt-serverless

```sh
> git clone https://github.com/kai-kou/nuxt-serverless.git
> cd nuxt-serverless
> npm install
```

必要となるライブラリのインストールと、不要となるライブラリをアンインストールします。

```sh
> npm install --save-dev serverless-apigw-binary
> npm install --save aws-serverless-express

> npm uninstall --save serverless-http
> npm uninstall --save-dev serverless-s3-sync
```

```serverless.yaml``` を編集します。S3に関する定義を削除、LambdaとAPI Gatewayで静的ファイルが返せるように定義を追加しています。```service``` や```region``` は任意でどうぞ。

```yaml:serverless.yml
service: nuxt-serverless # 1. Edit whole service name

provider:
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, 'dev'}
  region: ap-northeast-1 # 2. Edit AWS region name
  environment:
    NODE_ENV: production

custom:
  serverless-offline:
    port: 4000
  apigwBinary:
    types:
      - '*/*'

package:
  exclude:
    - src/**
  include:
    - serverless.yml

functions:
  nuxt-renderer:
    handler: handler.render
    memorySize: 512
    timeout: 30
    events:
      - http:
          path: /
          method: ANY
          cors: true
      - http:
          path: /{proxy+}
          method: ANY
          cors: true

plugins:
  - serverless-offline
  - serverless-apigw-binary
```

### 実装

```handler.js``` の実装を分割します。

ほぼ、記事通りの実装となります。

```js:handler.js
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./server')

const binaryMimeTypes = [
  'application/javascript',
  'application/json',
  'application/octet-stream',
  'application/xml',
  'font/eot',
  'font/opentype',
  'font/otf',
  'image/jpeg',
  'image/png',
  'image/svg+xml',
  'text/comma-separated-values',
  'text/css',
  'text/html',
  'text/javascript',
  'text/plain',
  'text/text',
  'text/xml'
]
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes)

module.exports.render = (event, context) => awsServerlessExpress.proxy(server, event, context)
```

```server.js``` にExpressの実装を引っ越しました。ポイントは```/_nuxt``` と```/static``` へのアクセス時にS3にリダイレクトさせていたのを、直接ファイルを返すようにしている点です。

前回同様に、ステージ名のパス対応も入れています。


```js:server.js
const express = require('express')
const {Nuxt} = require('nuxt')
const path = require('path')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')

const app = express()

app.use(awsServerlessExpressMiddleware.eventContext())

app.use('/_nuxt', express.static(path.join(__dirname, '.nuxt', 'dist', 'client')))

app.use('/static', express.static(path.join(__dirname, 'static')))

let config = require('./nuxt.config.js')
const nuxt = new Nuxt(config)

app.use((req, res) => {
  req.url = `${config.router.base}${req.url}`.replace('//', '/')
  nuxt.render(req, res)
})

module.exports = app
```

### デプロイ

デプロイしてみます。

```sh
> npm run sls:create
(略)
Entrypoint app = 55e8f003a83a598dd113.js 5e45cc8df25adbaac94b.js 1d1bc122ddb9c7b7f271.css 1417c3d6af1ef683305c.js
[15:20:26] Compiling server
[15:20:31] Compiled server in 5s

Hash: f66c1442df8e32ec8ab4
Version: webpack 4.25.1
Time: 4754ms
Built at: 2018-11-13 15:20:31
             Asset     Size  Chunks             Chunk Names
server-bundle.json  213 KiB          [emitted]
Entrypoint app = server-bundle.js
(略)
Serverless: Stack update finished...
Service Information
service: nuxt-serverless
stage: dev
region: ap-northeast-1
stack: nuxt-serverless-dev
api keys:
  None
endpoints:
  ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
  ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
functions:
  nuxt-renderer: nuxt-serverless-dev-nuxt-renderer
```

### 動作確認

デプロイができたら、ブラウザでアクセスしています。

https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/

<img width="1061" alt="Hello__Nuxt_Serverless_Template-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/a93e420a-416e-bb65-dcd1-40de6532cda9.png">
<img width="1057" alt="Hello__TypeScript-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/5093e4b8-59b4-2d47-bdc3-3839de9a00f1.png">
<img width="1056" alt="view-source_https___1h3yxb6qji_execute-api_ap-northeast-1_amazonaws_com_dev_.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/a830fd64-fbf2-3dd4-7213-cdc10bfc5d2e.png">


やったぜ。


## ハマりどころ

### API Gatewayのパス指定に気をつけよう

今回は参考にした記事だと、```serverless.yml``` ```events``` に```http``` 設定が1つだけですが、それだと```https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/``` にアクセスしても、API GatewayからAWS Lambdaへイベントが発火せず、```{"message":"Missing Authentication Token"}``` となりました。ここは前回参考にした記事の設定のままだとうまくいきました。

```yaml:OK
    events:
      - http:
          path: /
          method: ANY
          cors: true
      - http:
          path: /{proxy+}
          method: ANY
          cors: true
```

```yaml:だめ。1時間くらい悩んだ
    events:
      - http:
          path: /{proxy+}
          method: ANY
          cors: true
```

```yaml:これもだめ。もうだめだ
    events:
      - http: ANY {proxy+}
```

### タイムアウトエラーが発生したら```yarn``` を利用する

```npm``` でライブラリをインストールしていると、デプロイできても、URLへアクセスするとタイムアウトエラーになることがありました。その場合には、```yarn``` でライブラリをインストールし直すとうまく動きました。

```sh
> rm -rf node_modules/
> npm install -g yarn # yarnがインストールされていなかったら
> yarn
> yarn build
> sls deploy
```

## 注意点

注意点としては、静的ファイルが多くなるとAWS Lambdaのアップロード制限に引っかかる可能性があるかもしれません。

AWS Lambda の制限 - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html

> AWS Lambda デプロイメントの制限

> Lambda 関数デプロイパッケージのサイズ (圧縮 .zip/.jar ファイル)	: 50 MB
> リージョンあたりの、アップロードできるすべてのデプロイパッケージの合計サイズ	: 75 GB
> デプロイパッケージ (非圧縮 .zip/.jar サイズ) に圧縮できるコード/依存関係のサイズ: 250 MB


## まとめ

シンプルなSSR対応のサービスを提供するにはこちらのほうがシンプルですね。
プロジェクト次第で使い分けが良さそうです。


## 参考

Serverless-Side Rendering With AWS Lambda & NuxtJS
https://medium.com/@fernalvarez/serverless-side-rendering-with-aws-lambda-nuxtjs-b94d15782af5

Nuxt.js(v2.2.0)+TypeScriptなアプリをAWS Lambda+αにデプロイしてみた
https://qiita.com/kai_kou/items/5b1ebb9abc5d62545e04

jeehyukwon/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/jeehyukwon/nuxt-serverless