firebaseを使ってnuxtのサーバーサイドレンダリングを試す

# 概要

nuxt(ssr)をfirebaseにデプロイする自分用の手順

nuxtのサーバーサイドレンダリングは、firebaseのHostingの他にfunctionsも利用する必要がある（らしい）

手順通り実行することでデプロイは可能だが、その後どうしたらどうできるかまでは未検証

## 参考

https://note.com/hagure_melon/n/n2065f5e4c736

# 手順

## リポジトリ作成

リポジトリ名は`test02`にした

```
npx create-nuxt-app test02
```

```
✨  Generating Nuxt.js project in test02
? Project name: test02
? Programming language: TypeScript
? Package manager: Npm
? UI framework: Bootstrap Vue
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? gosshys
? Version control system: Git
```

- Nuxt.js modulesはいらない
- Liting tools, Testing frameworkは外した
- Rendering modeをSSRにする

できあがったらリポジトリのディレクトリへ移動する

```
cd test02
```

## firebaseの初期化

```
firebase init
```

```
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◉ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
❯◉ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features
 ◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
```

- Firestore, Functions, Hostingを選択
- Firestoreは必須ではない

```
? Please select an option: 
  Use an existing project 
❯ Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
  Don't set up a default project
```

- `Create a new project`を選択

```
? Please select an option: Create a new project
i  If you want to create a project in a Google Cloud organization or folder, please use "firebase projects:create" instead, and return to this command when you've created the project.
? Please specify a unique project id (warning: cannot be modified afterward) [6-30 characters]:
 test02-123456789
? What would you like to call your project? (defaults to your project ID) 
✔ Creating Google Cloud Platform project
✔ Adding Firebase resources to Google Cloud Platform project

🎉🎉🎉 Your Firebase project is ready! 🎉🎉🎉

Project information:
   - Project ID: test02-123456789
   - Project Name: test02-123456789
```

- `unique project id`はurlになるので、適当に一意になるように入力する

```
=== Firestore Setup

Error: It looks like you haven't used Cloud Firestore in this project before. Go to https://console.firebase.google.com/project/test02-123456789/firestore to create your Cloud Firestore database.
```

- Firestoreをコンソールから事前に作成しろと出るのでコンソール上のURLからコンソールに移動する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/9bb3f039-6e13-50fa-d1b7-4938df0022f1.png)

- `Create database`を押す


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/1a872a39-bb6a-422e-a940-9aa239d4be73.png)

- production modeを選択。test modeでもいい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/e1251c05-fd4d-fdfe-2622-0ab8751fc0ec.png)

- regionは`asia-northeast1`が東京、`asia-northeast2`が大阪
- ただし、下の方にもでてくるがfunctionsが`us-central1`縛りの現状を考えると、あえて`us-central1`にすべきな気もする

https://firebase.google.com/docs/functions/locations?hl=ja

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/136dd7c3-dcb5-622b-81ee-2f5424ff3670.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/679fce5a-cf46-3094-e7c2-0f2f8a0e18e8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/dbde2a5f-3538-4867-229e-b5705cf34877.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/dc6905e3-0989-32d5-f945-714fc60474d3.png)

できた

もう一度、firebase initを実行


```
firebase init
```

```
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: test02-123456789 (test02-123456789)
i  Using project test02-123456789 (test02-123456789)

=== Firestore Setup

Firestore Security Rules allow you to define how and when to allow
requests. You can keep these rules in your project directory
and publish them with firebase deploy.
```

```
? What file should be used for Firestore Rules? firestore.rules
```

```
? What file should be used for Firestore indexes? firestore.indexes.json
```

```
? What language would you like to use to write Cloud Functions? JavaScript
```

- ここはJavaScriptにしたが、Type scriptのやり方がわからなかっただけ

```
? Do you want to use ESLint to catch probable bugs and enforce style? No
```

ここは`N`とすべきらしい

```
? Do you want to install dependencies with npm now? Yes
```

```
? What do you want to use as your public directory? public
```

```
? Configure as a single-page app (rewrite all urls to /index.html)? No
```

ここは`N`らしい

```
? Set up automatic builds and deploys with GitHub? No
```

`N`としないと、Githubにリポジトリがないと進めなくなるようでした(弱気)

```
✔  Firebase initialization complete!
```

```
vi firebase.json
```

ここは参考ページのとおりに修正

```
{
 "functions": {
   "functions": {
     "source": "functions"
   }
 },
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "public": "static",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
   "rewrites": [
     {
       "source": "**",
       "function": "ssr"
     }
   ]
  }
}
```

## functionsの設定

```
cd functions
```

```
npm install --save nuxt
```

```
vi package.json
```

```
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "12"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^9.2.0",
    "firebase-functions": "^3.11.0",
    "bootstrap": "^4.5.2",
    "bootstrap-vue": "^2.17.3",
    "nuxt": "^2.14.8"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}
```

最初にbootstrapの依存を追加したのでここにも追加する必要がある
(バージョン等は上位ディレクトリのpackage.jsで確認できる)

```
    "bootstrap": "^4.5.2",
    "bootstrap-vue": "^2.17.3",
```

再度、npm install

```
npm install
```

※これをやらないとdeploy後に画面描画時にエラーがでる

```
vi index.js
```

```
const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//   functions.logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });

const { Nuxt } = require('nuxt');
const nuxt = new Nuxt({
   buildDir: 'ssr',
   dev: false
});

exports.ssr = functions.https.onRequest(async (req, res) => {
   await nuxt.ready()
   return nuxt.render(req, res)
})
```

ここで、cloud functionsのregionを東京にしたいという気持ちも出てくるが、以下の制約があるためregion指定はできない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/edf5a67c-5b1d-2a2a-f1dc-ac12787ebca7.png)
https://firebase.google.com/docs/functions/locations?hl=ja


上位ディレクトリに移動

```
cd ..
```

```
vi package.json
```

```
{
  "name": "test02",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build && npm run build:copy:ssr",
    "build:copy:ssr": "rimraf functions/ssr && mkdirp functions/ssr && cp -R .nuxt/dist functions/ssr/dist",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate"
  },
  "dependencies": {
    "@nuxt/typescript-runtime": "^2.0.0",
    "bootstrap": "^4.5.2",
    "bootstrap-vue": "^2.17.3",
    "core-js": "^3.6.5",
    "nuxt": "^2.14.6"
  },
  "devDependencies": {
    "@nuxt/types": "^2.14.6",
    "@nuxt/typescript-build": "^2.0.3"
  }
}
```

以下の部分を修正

```
    "build": "nuxt-ts build && npm run build:copy:ssr",
    "build:copy:ssr": "rimraf functions/ssr && mkdirp functions/ssr && cp -R .nuxt/dist functions/ssr/dist",
```

```
npm install
```

- これはいらないかも

## デプロイ(functions含む)

```
npm run build && firebase deploy
```

nodeのバージョンが10?以上だと、無料プランだとデプロイできない。8とか10はディスコンが決まっているので、デフォルトの12でデプロイするために、支払いアカウントを作成して、Blazeプランに変更する

```
Error: HTTP Error: 400, Boilling account for project '423603410634' is not found. Billing must be enabled for activation of service(s) 'cloudbuild.googleapis.com,containerregistry.googleapis.com' to proceed.
```

### 支払いアカウントを追加

Project Overviewのヨコのギアマークから`Usage and billing`を押す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/19b491ea-f548-dce9-cff8-13287d9c6233.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/d98527d3-1533-e534-4c4f-933ac801e300.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/b56bbafc-c313-ceb8-3f8c-e32cc632e01b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/d3cfc477-3c6d-a4ec-4c92-6ebda2436bb5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/25f2ca54-c584-ed80-9ef0-38f6906f335f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/495dba37-2ab0-5fa2-a8ef-d53ecd146a5e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/5cbf8f39-1624-7acc-ed5d-f87b01e7dd0c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/fc2ab889-0990-7b27-d6a4-9a6b679a2007.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/b90a6f7a-6c27-ec40-47ba-17668c2838d5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/e2e0b277-052c-7f8c-6c56-743983aeaab1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/da9deaa8-6b1b-ff3e-59e2-c2c2a20b48cf.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/3765c1fe-275b-5b6d-f9b3-602426ac9772.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/5dde22d7-c292-17d2-fc42-392943922734.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/e70e8029-4763-a1bd-70e0-2a995abd6adb.png)

### 再デプロイ

```
npm run build && firebase deploy
```

```
=== Deploying to 'test02-123456789'...

i  deploying firestore, functions, hosting
i  firestore: reading indexes from firestore.indexes.json...
i  cloud.firestore: checking firestore.rules for compilation errors...
✔  cloud.firestore: rules file firestore.rules compiled successfully
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
⚠  functions: missing required API cloudbuild.googleapis.com. Enabling now...
✔  functions: required API cloudfunctions.googleapis.com is enabled
✔  functions: required API cloudbuild.googleapis.com is enabled
✔  firestore: deployed indexes in firestore.indexes.json successfully
i  firestore: latest version of firestore.rules already up to date, skipping upload...
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (537.49 KB) for uploading
✔  functions: functions folder uploaded successfully
i  hosting[test02-123456789]: beginning deploy...
i  hosting[test02-123456789]: found 2 files in static
✔  hosting[test02-123456789]: file upload complete
✔  firestore: released rules firestore.rules to cloud.firestore
i  functions: creating Node.js 12 function ssr(us-central1)...
✔  functions[ssr(us-central1)]: Successful create operation. 
Function URL (ssr): https://us-central1-test02-123456789.cloudfunctions.net/ssr
i  hosting[test02-123456789]: finalizing version...
✔  hosting[test02-123456789]: version finalized
i  hosting[test02-123456789]: releasing new version...
✔  hosting[test02-123456789]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/test02-123456789/overview
Hosting URL: https://test02-123456789.web.app
```

できた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/8870d785-0ca3-2dbd-8cf6-eebed6e6afd6.png)

画面遷移も可能

## 付録

以下が表示されたら、GCP側の設定が必要

```
Your client does not have permission to get URL /ssr/ from this server.
```

https://qiita.com/toshiaki_takase/items/ce65cd5582a80917b52f