Nuxt.js（SSR）とGoogle App Engineだけでウェブアプリがつくれそう。

Nuxt.jsのSSRモードとGoogle App Engine（GAE）をつかうとウェブアプリがお手軽につくれそうだったので試してみました。

# Nuxt.jsのインストール

公式ドキュメントの `create-nuxt-app` でセットアップ

https://ja.nuxtjs.org/

- 今回はGAEでサーバサイドレンダリングしたいので、セットアップ時にSSRモードを選択
- インストールには、Yarn を使いました

## Nuxt.jsにデプロイ用設定

1. Nuxt.jsのルートディレクトリにGAEの設定ファイルをつくります
1. ドキュメントを参考にしました。（ https://ja.nuxtjs.org/docs/2.x/deployment/appengine-deployment/ ）

```yaml:app.yaml
runtime: nodejs14

instance_class: F1

handlers:
  - url: /_nuxt
    static_dir: .nuxt/dist/client
    secure: always

  - url: /(.*\.(gif|png|jpg|ico|txt))$
    static_files: static/\1
    upload: static/.*\.(gif|png|jpg|ico|txt)$
    secure: always

  - url: /.*
    script: auto
    secure: always

env_variables:
  HOST: '0.0.0.0'
```

基本は公式のサンプルのままですが少し変更しました

- runtimeですが、公式ではnodejs10を指定していましたが、この記事を書いている時点でGAEにnodejs14がリリースされていたので、バージョンを変更して14を指定しました。
- instance_class、今回は練習なので最小構成のF1です。


# Google App Engine の作成

公式ドキュメント

https://cloud.google.com/appengine/docs/standard/nodejs/quickstart?hl=ja

1.上記ドキュメントの[始める前に]の項目にある準備が終わったら
2.Nuxt.jsのルートドキュメントで初期化コマンド

```sh
$ gcloud app create --project=[YOUR_PROJECT_ID]
```

3.ビルドしてデプロイするためのファイルを書き出す

```sh
$ yarn build
```

4.デプロイ

```sh
$ gcloud app deploy
```

5.（以下のコマンドで開けます）

```sh
$ gcloud app browse
```

以上でデプロイ完了
すでにGAE上でNuxt.jsが起動して、ブラウザから閲覧できるようになっているはずです。
フルマネージド型のサーバーレスプラットフォームとはよく言ったもので、サーバ周りをまるっと担ってくれます。
とても手軽で便利です。

# Cloud Buildを使ってgithubリポジトリから自動的にデプロイ

さらにCloud Buildを使うと、githubにpushしたタイミングなどで自動的にデプロイできます。
トリガーは色々設定できますが、自分は決められた名前のtagをpushしたタイミングで発火するようにしました。

公式ドキュメント

https://cloud.google.com/build/docs/deploying-builds/deploy-appengine?hl=ja

## デプロイ前に自動処理

自動デプロイの設定が終わったら、デプロイ時に一緒に実行してほしい処理を設定していきます。
cloudbuild.yaml というファイルで指示しておくと、デプロイ時に自動実行してくれます。

今回は

- ビルド（Nuxt.jsからデプロイ用ファイルを書き出す）
- 環境変数の設定（リポジトリに保存したくない情報などをGAEに設定する）

の2点を実行するように設定しました。

### ビルド

ビルドコマンドでデプロイ用の静的JSファイルなどを作成します。

### 環境変数の設定

GAEで使う環境変数は `app.yaml` の `env_variables` に書いておくとGAE環境に設定することができます。
しかし、自動デプロイのためには app.yaml をリポジトリにアップロードしないといけないので、DB接続情報などオープンにしたくない情報を保存するのに不安が残ります。
そこで、CloudBuildに変数を設定しておき、デプロイ前にapp.yamlに書き出すようにします。

今回つくったcloudbuild.yamlです。これをプロジェクトのルートに設置します。

```yaml:cloudbuild.yaml
steps:
  - name: 'gcr.io/cloud-builders/gcloud'
    entrypoint: /bin/sh
    args:
      - '-c'
      - |
        cat << EOF > secret.yaml
        env_variables:
          DB_USER: $_DB_USER
          DB_PASSWORD: $_DB_PASSWORD
          DB_NAME: $_DB_NAME
        EOF
  - name: node
    entrypoint: yarn
    args:
      - 'install'
  - name: node
    entrypoint: yarn
    args:
      - 'build'
  - name: gcr.io/google.com/cloudsdktool/cloud-sdk
    id: Deploy
    entrypoint: gcloud
    args:
      - app
      - deploy
      - '--appyaml=${_APP_YAML}'
      - '--project=${PROJECT_ID}'
      - '--version=${_VERSION}'
      - '--quiet'

options:
  substitutionOption: ALLOW_LOOSE
substitutions:
  _APP_YAML: 'app.yaml'
  _VERSION: '${COMMIT_SHA}'

```

CloudBuidに設定した変数に合わせて `env_variables:` のところを書き換えます。

```yaml

    env_variables:
        GAEの環境変数名: $_CloudBuild変数名
        GAEの環境変数名: $_CloudBuild変数名

```

みたいな書き方でGAEに送り込みたい環境変数の分だけ設定します。
それを、`secret.yaml` に書き出して、`app.yaml` にincludeするという仕組みです。（下でやります）

これで

1. CloudBuildの変数を環境設定ファイルに書き出し
1. Nuxtをビルド
1. デプロイ

が実行されます。

### app.yaml修正

app.yamlの末尾に追加します。
cloudbuild.yamlで生成した環境変数をGAE設定ファイルにインクルードします。
これで、GAEデプロイ時に環境変数が設定されます。

```yaml:app.yaml
...

includes:
  - secret.yaml

```

### CloudBuild 参考サイト

Cloudbuildの設定で下記のサイトがとても参考になりました。

- [Next.jsをGAEで動かす（CloudBuildから自動デプロイ）](https://zenn.dev/catnose99/articles/353664a9fe1f0f)
- [GAEのapp.yamlで使う秘密環境変数をCloud Buildから渡す](https://sanshiro.dev/article/5746980898734080)


# Nuxt.jsでAPIサーバを構築

サーバ側の構築と開発環境ができたので、あとはNuxt.jsをつかってアプリを作っていきます。

- Nuxt.jsはSSRモードで構築するとserverMiddlewareが使えます。
- サーバ側で動作する共通処理を実装できるので、これをつかって簡単なAPIサーバーが構築できます。
- 他のフレームワークや外部のサーバーを使わなくても、GAEとNuxt.jsだけで全部できるのでお手軽です。

## serverMiddleware

Nuxt.js serverMiddlewareのドキュメント
[カスタム API エンドポイント](https://ja.nuxtjs.org/docs/2.x/configuration-glossary/configuration-servermiddleware/#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0-api-%E3%82%A8%E3%83%B3%E3%83%89%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88)
を参考に作ります。

- サンプルコードのままだと、express周りの使い方がすこし古いようでしたので新しいバージョンに合わせて修正しました。
- ついでにTypeScriptで書き直しました

```ts:server-middleware/rest.ts
import express from 'express'

const app = express()

app.use(express.urlencoded({ extended: true }))
app.all('/getJSON', (_req: express.Request, res: express.Response) => {
  res.json({ data: 'hello world' })
})

module.exports = app

```

Nuxtの設定ファイルを記述

```typescript:nuxt.config.js
serverMiddleware: [
  { path: "/api", handler: "~/server-middleware/rest.ts" },
],
```

これで、`/api/getJSON` がAPIサーバーとしてルーティングされて、JSONを返すようになりました。

```json:/api/getJSON
{
  "data" : "hello world"
}
```

簡単なAPIサーバーならNuxtだけで作れます。

# さいごに

とりあえず基本的なところのみでDBとの接続とかもしたんですがまとめていません。
GAEとNuxt.jsだけでウェブアプリケーションとその開発環境がつくれそうです。
いまさらだけどクラウドって楽しいですね。いろいろいじっていきたいです。