nuxt-serverlessについて調べてみた

# 背景

nuxtで作ったアプリをAWSにデプロイする方法について調べていたところ、*nuxt-serverless*というものがあることを知ったので、お勉強。

https://github.com/tonyfromundefined/nuxt-serverless


# *nuxt-serverless*

とりあえず、READMEを翻訳。

「サーバーレスフレームワークを使用したAWSサーバーレススタック（Lambda + API Gateway + S3）上のNuxt.jsサーバーレスサーバー側レンダリングスターター」

とのこと。

ざっと中のコードを見た感じ、nuxtプロジェクトのテンプレートになっていて、ここにServerless Frameworkの設定ファイル等があらかじめ配備されている形になっていました。

使い方としては、githubのコードをclone(もしくはダウンロード)してきたものをベースに、*src*の中を編集してnuxtアプリを作成し、*serverless.yml*を編集して自分用のデプロイ設定に変更後、*yarn deploy:???* コマンドでビルドとデプロイを実行する感じでしょうか。

# *nuxt-serverless* を試してみる

事前準備として、AWSの設定 (その前にAWS CLIをインストールしていなければインストール)
※あらかじめAWSにアカウント作っておく必要もあります。

```
aws configure
```

Githubから*nuxt-serverless*をクローン

```
git clone https://github.com/tonyfromundefined/nuxt-serverless.git
```

インストール

```
cd nuxt-serverless
yarn install
```

そのままローカルで動かしてみる

```
yarn dev
```

ブラウザでアクセスすると下のような感じ。

![nuxt-template.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/d05b4df2-274c-0a9f-c276-e1cb363bc39d.jpeg)

「typescript」と「nuxt」のリンクを押下すると下に遷移。

![nuxt-template-ts.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/764a3e7b-a8ba-af07-9062-858f63334374.jpeg)
![nuxt-template-nuxt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/4f49ca7c-eba3-3356-c166-b6018f933142.jpeg)


続いて、デプロイに向けた設定。
ちょっと長いので、*serverless.yml*を上から見ていく。

```serverless.yml
service: nuxt-serverless

plugins:
  - serverless-s3-sync
  - serverless-apigw-binary
  - serverless-dotenv-plugin

package:
  individually: true
  excludeDevDependencies: true

provider:
  name: aws
  runtime: nodejs10.x
  stage: ${opt:stage, 'dev'}
  region: us-east-1

custom:
  #######################################
  # Unique ID included in resource names.
  # Replace it with a random value for every first distribution.
  # https://www.random.org/strings/?num=1&len=6&digits=on&loweralpha=on&unique=on&format=html&rnd=new
  stackId: lxpmd3
  #######################################

(続く)
```

サービス名はデプロイするサービスに合わせて変更。(今回はそのままにしておく)

リージョンは東京に変更しておく。

```
  region: ap-northeast-1
```

*stackId*にランダムな文字を指定。
今回は指定の通りに下記で生成して設定。
https://www.random.org/strings/?num=1&len=6&digits=on&loweralpha=on&unique=on&format=html&rnd=new
ちなみにこの*stackId*で各リソースの一意性を担保しているようなので、必ず変更が必要。

```
  stackId: l7o0rg
```

他はそのままで良さそう。

続きを見ていく。次はS3関連の設定っぽい。
クライアント系のリソース(.nuxt/dist/client)は、S3に入れているのが分かる。
変更は必要なさそう。

```serverless.yml
(続き)

  buckets:
    ASSETS_BUCKET_NAME: ${self:service}-${self:custom.stackId}-${self:provider.stage}-assets
    STATIC_BUCKET_NAME: ${self:service}-${self:custom.stackId}-${self:provider.stage}-static
  s3Sync:
    - bucketName: ${self:custom.buckets.ASSETS_BUCKET_NAME}
      localDir: .nuxt/dist/client
    - bucketName: ${self:custom.buckets.STATIC_BUCKET_NAME}
      localDir: static
  apigwBinary:
    types:
      - '*/*'

(続く)
```

以降は*Lambda*関連の設定。
実行環境とか、*Lambda*に配備するファイルとかを指定している。
ここもそのままで良さそう。

```serverless.yml
(続き)

functions:
  renderer:
    name: ${self:service}-${self:custom.stackId}-${self:provider.stage}-renderer
    handler: .nuxt/dist/serverless.handler
    memorySize: 2048
    timeout: 30
    environment:
      NODE_ENV: production
    package:
      include:
        - .nuxt/dist/serverless.js
        - .nuxt/dist/server/**
      exclude:
        - .nuxt/**
        - src/**
        - app.js
        - nuxt.config.js
        - nuxt.d.ts
        - README.md
        - server.js
        - serverless.js
        - serverless.yml
        - tsconfig.json
        - tslint.json
        - webpack.config.js
        - yarn-error.log

(続く)
```

次に記載されているのは、内容的にAPI Gateway系の設定かな。ここもそのままで。

```serverless.yml
(続き)

    events:
      - http:
          path: /
          method: any
      - http:
          path: /{proxy+}
          method: any
      - http:
          path: /_nuxt/{proxy+}
          method: any
          integration: http-proxy
          request:
            uri: https://${self:custom.buckets.ASSETS_BUCKET_NAME}.s3.${self:provider.region}.amazonaws.com/{proxy}
            parameters:
              paths:
                proxy: true
      - http:
          path: /static/{proxy+}
          method: any
          integration: http-proxy
          request:
            uri: https://${self:custom.buckets.STATIC_BUCKET_NAME}.s3.${self:provider.region}.amazonaws.com/{proxy}
            parameters:
              paths:
                proxy: true

(続く)
```

以降は、*assets*用のバケット作成とポリシー割り当て。
中身は*AWS CloudFormation*の書式のようですね。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_S3.html
このあたりもそのままで大丈夫そう。

```serverless.yml
(続き)

resources:
  Resources:
    ClientAssetsBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:custom.buckets.ASSETS_BUCKET_NAME}
        CorsConfiguration:
          CorsRules:
            -
              AllowedOrigins:
                - '*'
              AllowedHeaders:
                - '*'
              AllowedMethods:
                - GET
                - HEAD
                - PUT
                - POST
                - DELETE
              MaxAge: 3000
              ExposedHeaders:
                - x-amz-server-side-encryption
                - x-amz-request-id
                - x-amz-id-2
    ClientAssetsBucketPolicy:
      Type: AWS::S3::BucketPolicy
      Properties:
        Bucket:
          Ref: ClientAssetsBucket
        PolicyDocument:
          Version: '2012-10-17'
          Statement: [
            {
              Action: ['s3:GetObject'],
              Effect: 'Allow',
              Resource: {
                Fn::Join: ['', ['arn:aws:s3:::', { Ref: 'ClientAssetsBucket' }, '/*']],
              },
              Principal: '*'
            },
          ]

(続く)
```

続いて*static*用のバケット作成とポリシー割り当て。上とほぼ同じ。変更もなし。

```serverless.yml
(続き)

    ClientStaticBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:custom.buckets.STATIC_BUCKET_NAME}
        CorsConfiguration:
          CorsRules:
            -
              AllowedOrigins:
                - '*'
              AllowedHeaders:
                - '*'
              AllowedMethods:
                - GET
                - HEAD
                - PUT
                - POST
                - DELETE
              MaxAge: 3000
              ExposedHeaders:
                - x-amz-server-side-encryption
                - x-amz-request-id
                - x-amz-id-2
    ClientStaticBucketPolicy:
      Type: AWS::S3::BucketPolicy
      Properties:
        Bucket:
          Ref: ClientStaticBucket
        PolicyDocument:
          Version: '2012-10-17'
          Statement: [
            {
              Action: ['s3:GetObject'],
              Effect: 'Allow',
              Resource: {
                Fn::Join: ['', ['arn:aws:s3:::', { Ref: 'ClientStaticBucket' }, '/*']],
              },
              Principal: '*'
            },
          ]
```
結局、変更したのは*region*と*stackId*だけ。
設定が完了したので、デプロイコマンドを実行してみる。

```
> yarn deploy:dev

yarn run v1.22.4
$ sls deploy --stage dev
Serverless: DOTENV: Loading environment variables from .env.development:
Serverless:      - NUXT_APP_STAGE
Serverless:      - NUXT_APP_GRAPHQL_ENDPOINT
Serverless:      - NUXT_APP_VERSION
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service renderer.zip file to S3 (24.85 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...............................................................
Serverless: Stack update finished...
Service Information
service: nuxt-serverless
stage: dev
region: ap-northeast-1
stack: nuxt-serverless-dev
resources: 21
api keys:
  None
endpoints:
  ANY - https://lyktihubdl.execute-api.ap-northeast-1.amazonaws.com/dev/
  ANY - https://lyktihubdl.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
  ANY - https://lyktihubdl.execute-api.ap-northeast-1.amazonaws.com/dev/_nuxt/{proxy+}
  ANY - https://lyktihubdl.execute-api.ap-northeast-1.amazonaws.com/dev/static/{proxy+}
functions:
  renderer: nuxt-serverless-l7o0rg-dev-renderer
layers:
  None
S3 Sync: Syncing directories and S3 prefixes...
.........
S3 Sync: Synced.
Serverless Enterprise: Run `serverless login` and deploy again to explore, monitor, secure your serverless project for free.
Done in 118.22s.
```

2分ほどでデプロイ完了。
表示されているURLにアクセスすると、

![nuxt-deploy.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/c3585f9d-a96c-78fc-b94f-4f24fcf7720b.jpeg)

ちゃんとメイン画面が表示された。

ただ、画面に表示されている「typescript」と「nuxt」のリンク先が正しく表示できず。

![nuxt-deploy-ts.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/7110f923-46f0-d10f-9f29-5f14e5d34aa3.jpeg)

READMEにも下のように書いてあるし、URLに{stage}が入ることでルーティングに失敗しているものと思う。

* Auto generated URL https://*.execute-api.{region}.amazonaws.com/{stage}/ will result in a JavaScript error. (routing problem) Please use the Custom Domain.

URLを～/typescriptから～/dev/typescriptに変更して、直接ブラウザのアドレスバーに入れたら表示された。

![nuxt-deploy-ts2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/a09891a4-09f7-3b20-ce5c-d496898efb89.jpeg)




最後に、作った環境を消しておく。

```
> yarn undeploy:dev

yarn run v1.22.4
$ sls remove --stage dev
Serverless: DOTENV: Loading environment variables from .env.development:
Serverless:      - NUXT_APP_STAGE
Serverless:      - NUXT_APP_GRAPHQL_ENDPOINT
Serverless:      - NUXT_APP_VERSION
S3 Sync: Removing S3 objects...
...
S3 Sync: Removed.
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
.......................................
Serverless: Stack removal finished...
Done in 24.49s.
```

S3バケットも消える。

# 参考にしたサイト

https://blog.potproject.net/2019/10/03/nuxt-serverless-tips