firebase functionsでnuxt.js v2.11.0をSSR

## 前説
nuxt.js の Minimum required node version は `8.9.0` の筈 [^1] なのだが
`create-nuxt-app` すると以下のようなエラーになりプロジェクトが生成されない
> error semver@7.1.1: The engine "node" is incompatible with this module. Expected version ">=10". Got "8.17.0"

firebase functions の node.js バージョンが 10 を利用できる [^2] ようなので作業ログを残す

## 事前準備
### ローカルに node v10 実行環境を整える
```bash
$ nodebrew install-binary v10
$ nodebrew use v10
$ node -v
v10.18.1
```

### Firebase CLI をインストールしてログイン
https://firebase.google.com/docs/cli?hl=ja

### firebase にプロジェクトを作る
`firebase init` する際に新しいプロジェクトを作成だと上手く行かなかった事があるので事前に用意しておく

![console.firebase.google.com_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12238/dd933772-90e6-600f-a784-9b51e9e39165.png)

## create-nuxt-app
`Vuetify.js` なのは趣味
`Axios` なども全部盛りで行く（一番いいのを頼む

```bash
$ yarn create nuxt-app nuxt-sandbox
yarn create v1.19.1
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-nuxt-app@2.12.0" with binaries:
      - create-nuxt-app

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in nuxt-sandbox
? Project name nuxt-sandbox
? Project description My supreme Nuxt.js project
? Author name hirose504
? Choose the package manager Yarn
? Choose UI framework Vuetify.js
? Choose custom server framework Express
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, StyleLint
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

yarn run v1.19.1
$ eslint --ext .js,.vue --ignore-path .gitignore . --fix
✨  Done in 4.68s.

🎉  Successfully created project nuxt-sandbox
```

## firebase init hosting
`public directory` を `static` にしているので注意

```bash
$ firebase init hosting

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/khirose/workspace/nuxt-sandbox


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: nuxt-sandbox-44583 (nuxt-sandbox)
i  Using project nuxt-sandbox-44583 (nuxt-sandbox)

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? static
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
✔  Wrote static/index.html

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

### rm static/index.html
いらないので消しておく

## firebase init functions
```bash
$ firebase init functions

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/khirose/workspace/nuxt-sandbox

Before we get started, keep in mind:

  * You are initializing in an existing Firebase project directory


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

i  .firebaserc already has a default project, using nuxt-sandbox-44583.

=== Functions Setup

A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? No
✔  Wrote functions/package.json
✔  Wrote functions/index.js
✔  Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes

> protobufjs@6.8.8 postinstall /Users/khirose/workspace/nuxt-sandbox/functions/node_modules/protobufjs
> node scripts/postinstall

npm notice created a lockfile as package-lock.json. You should commit this file.
added 250 packages from 188 contributors and audited 801 packages in 8.057s

24 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

## nuxt.js の buildDir を functions/nuxt に設定する
```diff
      ** You can extend webpack config here
      */
     extend(config, ctx) {}
-  }
+  },
+  buildDir: 'functions/nuxt'
 }
```

## yarn build
```bash
$ yarn build
yarn run v1.19.1
$ nuxt build
ℹ Production build                                                                                                                          00:11:45
✔ Builder initialized                                                                                                                       00:11:45
✔ Nuxt files generated                                                                                                                      00:11:45

✔ Client
  Compiled successfully in 39.16s

✔ Server
  Compiled successfully in 36.43s


Hash: fcc7191e683a7ecdca4e
Version: webpack 4.41.5
Time: 39160ms
Built at: 2020-01-21 12:12:26 AM
                         Asset       Size  Chunks                                Chunk Names
../server/client.manifest.json   13.8 KiB          [emitted]
       0f656e17fb54ebe20446.js   2.35 KiB       4  [emitted] [immutable]         runtime
       2f4e594da4cdc22175c6.js   41.8 KiB       0  [emitted] [immutable]         app
       82522d301b50c003f76b.js   24.8 KiB       2  [emitted] [immutable]         pages/index
       92e180a159792bf5253b.js    417 KiB       5  [emitted] [immutable]  [big]  vendors.app
       9de66b89ffe9f31a420d.js    4.3 KiB       6  [emitted] [immutable]
                      LICENSES  389 bytes          [emitted]
       c4f7a32247859cd1b96e.js    171 KiB       1  [emitted] [immutable]         commons.app
       d776723d0d7c2b289b88.js  817 bytes       3  [emitted] [immutable]         pages/inspire
     icons/icon_120.5f6a36.png   4.68 KiB          [emitted]
     icons/icon_144.5f6a36.png    5.8 KiB          [emitted]
     icons/icon_152.5f6a36.png    6.1 KiB          [emitted]
     icons/icon_192.5f6a36.png   7.83 KiB          [emitted]
     icons/icon_384.5f6a36.png   18.1 KiB          [emitted]
     icons/icon_512.5f6a36.png     20 KiB          [emitted]
      icons/icon_64.5f6a36.png   2.35 KiB          [emitted]
        manifest.027089c3.json  778 bytes          [emitted]
 + 2 hidden assets
Entrypoint app = 0f656e17fb54ebe20446.js c4f7a32247859cd1b96e.js 92e180a159792bf5253b.js 2f4e594da4cdc22175c6.js

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
  92e180a159792bf5253b.js (417 KiB)

Hash: 16b94bb282dcc0eb88ed
Version: webpack 4.41.5
Time: 36435ms
Built at: 2020-01-21 12:13:03 AM
                  Asset       Size  Chunks                         Chunk Names
cfbe8323351feaf2e082.js   23.4 KiB       1  [emitted] [immutable]  pages/index
e59f79c261b3844d5477.js  793 bytes       2  [emitted] [immutable]  pages/inspire
              server.js    414 KiB       0  [emitted]              app
   server.manifest.json  203 bytes          [emitted]
Entrypoint app = server.js
✨  Done in 82.64s.
```

### functions/.gitignore
```
node_modules/
nuxt/
```

### .eslintignore
```
functions/nuxt
```

## functions/index.js を編集
```javascript
const functions = require('firebase-functions')
const express = require('express')
const { Nuxt } = require('nuxt')
const app = express()

// Import and Set Nuxt.js options
const config = {
  dev: false,
  buildDir: 'nuxt'
}

// Init Nuxt.js
const nuxt = new Nuxt(config)

app.use(async (req, res) => {
  await nuxt.ready()
  nuxt.render(req, res)
})

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions

exports.nuxtApp = functions.https.onRequest(app)

exports.helloWorld = functions.https.onRequest((request, response) => {
  response.send('Hello from Firebase!')
})
```

## firebase.json の rewrites を編集
```diff
     "rewrites": [
       {
         "source": "**",
-        "destination": "/index.html"
+        "function": "nuxtApp"
       }
     ]
   }

```

## functions/package.json を編集して node v10 で起動するようにする
```diff
     "logs": "firebase functions:log"
   },
   "engines": {
-    "node": "8"
+    "node": "10"
   },
   "dependencies": {
     "firebase-admin": "^8.6.0",
```

## ローカルで動作確認
rewrites 設定を書き換えてあるので、ホスティングローカルサーバー http://localhost:5000 にアクセスするとローカルの functions 上の nuxtApp にリダイレクトされる

```bash
$ firebase serve --only functions,hosting
✔  functions: Using node@10 from host.
✔  functions: Emulator started at http://localhost:5001
i  functions: Watching "/Users/khirose/workspace/nuxt-sandbox/functions" for Cloud Functions...
i  hosting: Serving hosting files from: static
✔  hosting: Local server: http://localhost:5000
✔  functions[nuxtApp]: http function initialized (http://localhost:5001/nuxt-sandbox-44583/us-central1/nuxtApp).
✔  functions[helloWorld]: http function initialized (http://localhost:5001/nuxt-sandbox-44583/us-central1/helloWorld).
[hosting] Rewriting / to http://localhost:5001/nuxt-sandbox-44583/us-central1/nuxtApp for local Function nuxtApp
i  functions: Beginning execution of "nuxtApp"
127.0.0.1 - - [20/Jan/2020:15:30:34 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
i  functions: Finished "nuxtApp" in ~1s
127.0.0.1 - - [20/Jan/2020:15:30:37 +0000] "GET /sw.js HTTP/1.1" 200 995 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
```

## functions ディレクトリ内で npm install
firebase functions 上で動作させるために、依存関係の追加が必要

```bash
$ npm install nuxt @nuxtjs/axios @nuxtjs/pwa @nuxtjs/dotenv

+ @nuxtjs/axios@5.9.3
+ @nuxtjs/pwa@3.0.0-beta.19
+ nuxt@2.11.0
+ @nuxtjs/dotenv@1.4.1
added 840 packages from 413 contributors, updated 3 packages and audited 11469 packages in 42.787s
```

## firebase deploy
```bash
firebase deploy --only functions,hosting

=== Deploying to 'nuxt-sandbox-44583'...

i  deploying functions, hosting
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (509.49 KB) for uploading
✔  functions: functions folder uploaded successfully
i  hosting[nuxt-sandbox-44583]: beginning deploy...
i  hosting[nuxt-sandbox-44583]: found 5 files in static
✔  hosting[nuxt-sandbox-44583]: file upload complete
i  functions: creating Node.js 10 (Beta) function nuxtApp(us-central1)...
i  functions: creating Node.js 10 (Beta) function helloWorld(us-central1)...
✔  functions[nuxtApp(us-central1)]: Successful create operation.
Function URL (nuxtApp): https://us-central1-nuxt-sandbox-44583.cloudfunctions.net/nuxtApp
✔  functions[helloWorld(us-central1)]: Successful create operation.
Function URL (helloWorld): https://us-central1-nuxt-sandbox-44583.cloudfunctions.net/helloWorld
i  hosting[nuxt-sandbox-44583]: finalizing version...
✔  hosting[nuxt-sandbox-44583]: version finalized
i  hosting[nuxt-sandbox-44583]: releasing new version...
✔  hosting[nuxt-sandbox-44583]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/nuxt-sandbox-44583/overview
Hosting URL: https://nuxt-sandbox-44583.firebaseapp.com
```

## 結果
https://nuxt-sandbox-44583.firebaseapp.com/
https://github.com/hirose504/nuxt-sandbox

## 参考にした記事
- [Firebase公式動画を参考にFirebaseとNuxt.jsを使ってSSRをServerlessで実現する - Qiita](https://qiita.com/wataruoguchi/items/fa9b59e21c4212293c04)
- [SSR モードの Nuxt.js を Firebase に Deploy する Tutorial - Qiita](https://qiita.com/okamuuu/items/c9f2989241af4a1bf588)

[^1]: https://github.com/nuxt/nuxt.js/releases/tag/v2.9.0
[^2]: https://firebase.google.com/docs/functions/manage-functions?hl=ja#set_nodejs_version