FirebaseとNuxt.jsで、CRUDのベストプラクティスを作ってみる。（主にFirebaseまわりについて）

FirebaseとNuxt.jsで、CRUDのベストプラクティスを作ってみます。

できあがったもののプレビューはこちらから
■nuxt-basic-crud2
https://nuxt-basic-crud2.firebaseapp.com

作ったファイルは以下にアップしています。
■dog-ears/nuxt-basic-crud
https://github.com/dog-ears/nuxt-basic-crud

【環境】
・Windows10
・Visual Studio Code
・Node（Nodist）

ホスティングは、firebase hostingを使用する。
データベースは、Cloud Firestoreを使用する。

Nuxtは、サーバーサイドレンダリング（SSR）させる。
yarnは使わず、npmを使う。


# （１）環境の準備
###1) Visual Studio Codeをインストール

###2) Nodeは、Nodistを使用。

■Nodist（公式）
https://github.com/nullivex/nodist/releases

2020年2月現在、Cloud FunctionsのNode.jsのバージョンは、10.15.3

■The Node.js 10 Runtime（英語）
https://cloud.google.com/functions/docs/concepts/nodejs-10-runtime?hl=en

nodistインストール後、下記実行

```
nodist + 10.15.3
nodist 10.15.3
```

nodeのバージョンは、10.15.3
npmのバージョンは、6.4.1
npxのバージョンは、10.2.0

#（２）Firebase の準備およびhostingテストまで。
###1) firebaseコンソールでやること
firebaseコンソールをブラウザで開く
https://console.firebase.google.com

googleアカウントでログインする。

プロジェクトの作成
googleアナリティクスは使用するとする。

Databaseを選択し、「データベースの作成」を選ぶ
「テストモードで開始」を選んで、「有効にする」を押す

Hostingを選択し、始めるを選ぶ。

###2)Firebase CLIのインストールおよびデプロイまで
ローカルにプロジェクトフォルダを作成する。
以後、[project_folder]とする。

Visual Studio Codeで、プロジェクトフォルダを開きコンソールから、以下実行

CLIのインストール

```
npm install -g firebase-tools
```

ログインと初期化

```
firebase login
firebase init
```

```
? Are you ready to proceed?
→ Y

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. (Press <space> to select)
→ Firestore、Functions、Hosting

? Select a default Firebase project for this directory:
→Use an existing project
→ 先ほど作ったプロジェクトを指定

— Firestore関連の質問 —

? What file should be used for Firestore Rules? (firestore.rules)
→ そのままenter

? What file should be used for Firestore indexes? (firestore.indexes.json)
→ そのままenter

— Functions関連の質問 —

? 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? (y/N)
→ n

? Do you want to install dependencies with npm now?
→ Y

— Hosting関連の質問 —

? What do you want to use as your public directory? (public)
→ そのままenter

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

これで、Firebaseの初期化が完了しました。
ディレクトリ構成は以下のようになっているかと思います。


 
[プロジェクトディレクトリ]
┣functions
┃┣node_modelus
┃┣.gitignore
┃┣index.js
┃┣package-lock.json
┃┗package.json
┣public
┃┣404.html
┃┗index.html
┣.firebaserc
┣.gitignore
┣firebase.json
┣firestore.indexes.json
┗firestore.rules

```
firebase serve
```

で、ローカルサーバーが立ち上がり、
http://localhost:5001
で、functionsのページが、
http://localhost:5000
で、Welcome Firebase Hosting Setup Completeのページが開きます。

ctrl + cでいったんサーバーを終了。

```
firebase deploy
```

で、
https://[プロジェクト名].firebaseapp.com/
にページがアップされます。

#（３）Functions の準備およびHostingからFunctionsを呼ぶまで。
###1) Node10ランタイムの設定
（参考）SSR モードの Nuxt.js を Firebase に Deploy する Tutorial
https://qiita.com/okamuuu/items/c9f2989241af4a1bf588

node10ランタイムを使えるようにします。

/functions/package.json

```
"engines": {"node": "8"}
↓
"engines": {"node": "10"}
```

###2) FunctionのhelloWorldテスト
/functions/index.js内に、helloWorldがコメントアウトされているので、
こちらを有効化します。
ついでに、function名をhelloWorldからnuxtServerに変えておきます。

```
/functions/index.js

// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
↓
exports.nuxtServer = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
```

ローカルサーバーで確認してみる。

```
firebase serve --only functions:nuxtServer
```

http://localhost:5000/[プロジェクト名]/us-central1/nuxtServer

無事、Hello from Firebase!が表示されました。

###3) HostingからFunctionsを呼び出す。
次に、hostingから、functionsを呼ぶようにします。

/firebase.json

```
"hosting": {
[中略]
  "rewrites": [
    {
      "source": "**/*.*",
      "destination": "/404.html"
    },
    {
      "source": "**",
      "function": "nuxtServer"
    }
  ]
}
}
```

※rewritesの上の行の]の後に、「,」を足すのを忘れずに。

拡張子付きのアドレスにアクセスした場合、ファイルがあれば表示。
なければ、notFoundページを表示するようにしました。
それ以外のアドレスは、すべてnuxtServerファンクションを呼ぶようにしてます。

ローカルテスト

```
firebase serve --only functions:nuxtServer,hosting
```

http://localhost:5000/hoge
などを開くと、helloWorldが実行されます。
※ただし、ルートに関しては、/public/index.htmlがあるため、そちらの表示が優先されます。

確認後、/publicフォルダの中にあるindex.htmlファイルは、不要なので、削除します。

#（４）functions下にnuxtをインストールする
functions下にnuxtをcliでインストールしようと思います。
ただ、普通にfunctionsフォルダで、

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

を実行すると、package.json等が上書きされてしまうので、
いったん下の階層（nuxt-src）にインストールします。

Visual Studio Codeのターミナルで、下記実行

```
cd functions
npx create-nuxt-app nuxt-src
```

```
? Project name
→ なんでもOK

? Project description
→ なんでもOK

? Author name
→ なんでもOK

? Choose a package manager
→ Npm

? Use a custom UI framework (Use arrow keys)
→ Buefy

? Use a custom server framework (Use arrow keys)
→ none

? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
→DotEnvのみ選択

? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
→ ESLint、Prettierを選択

? Use a custom test framework (Use arrow keys)
→ none

? Choose rendering mode (Use arrow keys)
→ Universal

? Choose development tools
→ jsconfig.json
```

インストール完了後、

```
cd ./nuxt-src
npm run dev
```

を実行すると、.nuxtフォルダが生成され、
ローカルサーバーが立ち上がり

http://localhost:3000/

で、テストページを確認できました。

ローカルサーバーは、ctrl+cでいったん停止しておきます。
/functions/nuxt-src/.nuxtフォルダもいったん削除。

次にnuxtのルートフォルダを一つ上のfunctionsに移動します。

functions/nuxt-src/package.jsonから
functions/package.jsonに、下記のように表記を移動します。

■scripts
→ そのまま全部追加。

startのみ、ダブるのでもともとあったfunctionsのstartは削除します。

```
"start": "npm run shell", // → こちらを削除
"start": "nuxt start",
```

ついでに、npm run serveとnpm run deployで、hostingもサーブ・デプロイするように変更しておきます。

```
"serve": "firebase serve --only functions",
"deploy": "firebase deploy --only functions",
↓
"serve": "firebase serve --only functions,hosting",
"deploy": "firebase deploy --only functions,hosting",
```

■dependencies、devDependencies
→ まるごと移動でOK

移動が完了したら、
functions/nuxt-src/内の
・node_modules
・package.json
・package-lock.json
・.git ※あれば
は削除します。

nuxt-srcに残ったファイル（※フォルダはそのまま）を上の階層に移動
.editorconfig
.env
.eslintrc.js
.gitignore（※上書きでOK）
.prettierrc
jsconfig.json
nuxt.config.js
README.md

移動したnuxt.config.jsに、以下追加。

```
srcDir: 'nuxt-src'
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
↓
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {},
publicPath: '/assets/'
},
```

上記すべて終わったら、functionsフォルダで

```
npm install
```

を実行しておく。

npm run devすると、
WARN No .env file found in …
が出るので、.env関連を修正する。

nuxt用の.envモジュール「@nuxtjs/dotenv」
公式を見ると、
modulesではなく、buildModulesに入れろと書いてあったので、
buildModdulesに移動しつつ、オプションでパスの指定をします。

``` 
buildModules: [
....（中略）
// Doc: https://github.com/nuxt-community/dotenv-module
['@nuxtjs/dotenv', { path: __dirname }]
],
```

#（５）ESlintがらみの設定変更
nuxtをインストールした時点で、
基本的なルールは設定されており、

```
npm run lint
```

で、lintチェックを実行できます。

実際に、lintチェックを実行したところ、
\functions\index.js
で、エラーが３つほど出ました。

修正をするにあたり、
まず、functions下に移動した.eslintrc.jsと.prettierrcを
VisualStudioCodeで使えるようにします。

※拡張機能のESLint、Prettierはインストールしておく。

■VisualStudioCodeでESLint拡張機能が動かない気がする時に確認したこと
https://note.kiriukun.com/entry/20190817-eslint-not-working-in-vscode

設定 – eslint.workingDirectories
をワークスペースで定義する。

```
settings.json
"eslint.workingDirectories": [
  "./functions"
]
```

これでfunctions/index.htmlを開くと、
エラー箇所に赤波線が表示されます。
赤波線にカーソルを合わせて、「クイックフィックス」→fix this pretier/pretier problem
で、自動的に修正されます。
 
次に、基本ルールですが、
初期設定でconsole.logが使用禁止のため、
このルールを修正します。

functions/.eslintrc.js

```
rules: {
'no-console': 'off',
```

上記のルールで、console出力があってもエラー表示をしなくなります。

#（６）function「nuxtServer」で、nuxtをよびだしレンダリングする。
/functions/index.js
を、下記のように変更

https://github.com/dog-ears/nuxt-basic-crud/blob/master/functions/index.js

コンフィグ内、@nuxtjs/eslint-moduleは残していると、デプロイ後に
Error: could not handle the request
が、発生する。
（ログを見ると、FATAL Cannot find module ‘@nuxtjs/eslint-module’と表示される）
なので、コンフィグを読み込んだ後に、@nuxtjs/eslint-moduleを除外する。

また、index.jsでrequireを使ってnuxt.config.jsを読み込むため、
nuxt.config.jsを以下のように修正します。

```
export default {
↓
module.exports = {
```

この時点で、npm run lint で、チェックすると、

```
1:1 error Unexpected module.exports, please use export default instead nuxt/no-cjs-in-config
```

が、出る。

CommonJSのrequireを使うなというエラー。
ルールから除外する。

/functions/.eslintrc.js
のrulesに、下記追加

```
'nuxt/no-cjs-in-config': 'off',
```

エラーが出なくなった。

nuxtをビルドする

```
npm run build
```

ローカルでfirebase サーバーを確認してみる

```
npm run serve
```

デプロイしてみる

```
npm run deploy
```

→見える（ただしエラーあり。）
/assetsにクライアントスクリプトを探しに行ってしまうので、
.nuxt/dist/clientの中身を/public/assetsにコピー。
これで、エラーが出なくなります。

npm buildしたときに自動的に.nuxt/dist/clientのなかみを
/public/assetsにコピーするようにする

```
npm install --save-dev cpx rimraf
```

下記ファイルを修正

/functions/package.json

```
"scripts": {
  "postbuild": "npm run remove-assets && npm run copy-assets",
  "remove-assets": "rimraf ../public/assets",
  "copy-assets": "cpx ./.nuxt/dist/client/** ../public/assets",
```

これで npm run buildを実行すると、
/public/assets
内が、自動的に更新される。

/public/assetsは、git対象外にするので、.gitignoreに
public/assets
を追加します。

#（７）nuxtを修正する。
必要パッケージを入れる

```
npm install dayjs firebase htmlspecialchars vuexfire
```

.envの設定

デフォルトの設定では、サーバーサイドでしか読み取れない。
下記の対応をすることで、クライアントサイドでも読み取れるようになる。

/functions/nuxt.config.js
先頭に以下追加

```
require('dotenv').config()
```

Nuxtの修正内容は、以下のコミット履歴から確認できます。

https://github.com/dog-ears/nuxt-basic-crud/commit/819eb8fb5b179deab9abec82365374b9a9d94d4c

だいぶ投稿が長くなってしまったので、Nuxtの詳細については割愛します。

リポジトリをクローンして、.env.sampleから.envファイルを作って、
npm run devすれば、コンソールで処理の流れが終えるようにしているので、
見ていただければと思います。