takeda_san’s blog

KotlinとVRを頑張っていく方向。

Firebaseで業務アプリケーションを構築しよう 2:Firebase Functions&Hostingにデプロイと動作確認

シリーズ

前回のあらすじ

Nuxt.jsのセットアップをして動作確認をしました。
takeda-san.hatenablog.com

Firebaseへのデプロイと動作確認

今回はNuxt.jsのアプリケーションをFirebase用にデプロイして、同じように動作確認をします。

firebase-toolsのインストール

Firebaseの初期設定をするには、Firebase CLIが必要です。
npm経由でFirebase CLIをインストールします。
お好みですが、今回はグローバルに入れておきます。

npm install -g firebase-tools
firebase login

ここの手順そのまんまです。 Firebase CLI リファレンス  |  Firebase

Firebaseのプロジェクトを作成する

早速、初期設定を…とやりたいところですがその前に、Firebase上のプロジェクトを作成しましょう。
Firebaseのページの左上の プロジェクトを追加 から新規作成ができます。

f:id:takeda_san:20191024002859p:plain (良き感じにただ言われるがままに同意する者としてチェックを入れたり、プロジェクト名を決めたりします)

無事プロジェクトが作成出来たらこんな画面が出るはず。
f:id:takeda_san:20191024003201p:plain

ついでにCloud Firestoreも後で使うので初期化しておきましょう。

Firebaseの初期設定

前回作ったアプリケーション内でFirebaseの初期設定をします。
先ほど作成したFirebase上のプロジェクトと紐づけるイメージです。

cd <プロジェクトのディレクトリ>
firebase init

対話形式でプロジェクトの設定を聞かれますが、

? Are you ready to proceed? <- 元気よくY
? Which Firebase CLI features do you want to set up for this folder? <- 今回はFirestore/Functions/Hostingを使います
? Please select an option: <- Use an existing project 
? Select a default Firebase project for this directory: <- プロジェクトは先ほど作成したものを指定します

とここまでやると、無事に…エラーが出ます。
locationを設定せよといわれてます。

Error: Cloud resource location is not set for this project but the operation you are attempting to perform in Cloud Firestore requires it. Please see this documentation for more details: https://firebase.google.com/docs/projects/locations

プロジェクトの設定(歯車マーク)から、 Google Cloud Platform(GCP)リソース ロケーション を設定します。
適当に近場のasia-northeast1にしておきましょう。
f:id:takeda_san:20191024004348p:plain

気を取り直してもう一度 firebase init しましょう。

? 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? <- TypeScript
? Do you want to use TSLint to catch probable bugs and enforce style? <- お好みで(今回はNoにしました)
? Do you want to install dependencies with npm now? <- y
? What do you want to use as your public directory? <- デフォルト public(後で設定変えますがとりあえずはこれ)
? Configure as a single-page app (rewrite all urls to /index.html)? <- y(後で設定変えますがとりあえずはこれ)

ここまでで初期設定が完了して、各種Firebase用のファイルが追加されているはず。

f:id:takeda_san:20191024005546p:plain

rewriteルールの設定とhosting

前回まではローカルでNuxt.jsを動かしてSSRをしていましたが、今回はその部分をFunctions上で行います。
そのため、rewrite ルールの設定とHostingの設定を前回作成したNuxt.jsアプリ用のものに変更します。

こちらの構成図が非常にわかりやすいです。
これを目指します。
Nuxt.jsとFirebaseでSPA×SSR×PWA×サーバーレスを実現する - DMM inside

設定はfirebase.jsonにあるので、それを変更します。

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "site": "<プロジェクトID>",
    "public": "app/static",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "ssr"
      }
    ]
  },
  "functions": {
    "source": "functions"
  }
}

変更したのは2点。

Functionsの処理を設定

ssrのエンドポイントにアクセスされたときの動作を定義します。
functions/src/index.ts を編集。

const functions = require('firebase-functions')
const { Nuxt } = require('nuxt')

const nuxt = new Nuxt({ buildDir: 'ssr', dev: false })
exports.ssr = functions.https.onRequest(nuxt.render)

nuxtのレンダリング結果を返すだけです。
(importで書きたいのだがわからん…)

ビルドとデプロイ設定

ビルドとデプロイ設定

ソースディレクトリのパスの設定

前回忘れていたのですが、jsconfig.jsonとtsconfig.jsonのパス設定を変えましょう。
それぞれディレクトリにappを付与しておきます。

jsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["./app/*"],
      "@/*": ["./app/*"],
      "~~/*": ["./app/*"],
      "@@/*": ["./app/*"]
    }
  },
  "exclude": ["node_modules", ".nuxt", "dist"]
}

tsconfig.json

    "paths": {
      "~/*": [
        "./app/*"
      ],
      "@/*": [
        "./app/*"
      ]
    },

各種パッケージインストール

ビルドに使うパッケージを入れておきましょう。
(WindowsでもLinuxライクなコマンドが使える!素敵!)

npm install -dev rimraf mkdirp cpx

functions配下にNuxt.jsを動作させるのに必要なパッケージを入れておきます。
プロジェクトルートに置いてあるpackage.jsonのdependenciesにあるものが入っていれば大丈夫です。

今までの設定だとこれを入れれば大丈夫なはず。

cd functions
npm install nuxt @nuxt/typescript-runtime @nuxtjs/axios @nuxtjs/eslint-config-typescript element-ui

ここまでで、functions配下のdependenciesはこんな感じになってます。

  "dependencies": {
    "@nuxt/typescript-runtime": "^0.2.3",
    "@nuxtjs/axios": "^5.8.0",
    "@nuxtjs/eslint-config-typescript": "^0.1.3",
    "element-ui": "^2.12.0",
    "firebase-admin": "^8.0.0",
    "firebase-functions": "^3.1.0",
    "nuxt": "^2.10.2"
  },

ビルドコマンドの設定

package.jsonのscriptの部分を書き換えます。

"scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build && npm run build:copy:ssr",
    "build:copy:ssr": "rimraf functions/ssr && mkdirp functions/ssr && cpx .nuxt/dist/** functions/ssr/dist",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
    "deploy": "firebase deploy",
    "serve": "firebase serve",
    "lint": "eslint --fix --ext .ts,.js,.vue --ignore-path .gitignore .",
    "test": "jest"
},

nuxt-tsでビルドして生成されたファイル群をfunctions/ssr配下にコピーして、firebase deployでデプロイしてます。

動作確認

functionsとnuxtそれぞれをビルド後のデプロイで動作確認してみましょう。
(functions配下も一気にbuildできるようにしたい…)

cd functions
npm run build
cd ..
npm run build  
npm run deploy

ログでこんなのが出るので、Hosting URLにアクセスします。

+  Deploy complete!

Project Console: https://console.firebase.google.com/project/<プロジェクトID>/overview
Hosting URL: https://<プロジェクトID>.firebaseapp.com

ローカル実行と同じ画面が表示されたら、無事成功でございます。
f:id:takeda_san:20191105081547p:plain

今回の変更点はこちら。
firebase設定の追加 · takedasan/yukito@b7306f3 · GitHub

おハマリ申しポイント

構築時にハマったポイントです。

Error: HTTP Error: 400, Project ‘’ is not a Cloud Firestore enabled project.

FirebaseのコンソールでCloud Firestoreを有効化しましょう。
サイドメニューのDatabaseからCloud Firestoreを選択して 有効化 ボタンで解消します。

なんかHosting URLにアクセスしたけど404になる

下記に書いてある通りなんですが、hostingから動的コンテンツを返すときは、今のところFunctionsでus-central1リージョンを使う必要がありそうです。
素直にデフォルトのus-central1にしましょう。
Cloud Functions locations  |  Firebase

なんかFunctionsが500エラーで落ちてる

Function配下にも、プロジェクトルートに置いてあるpackage.jsonのdependenciesと同等のパッケージのインストールが必要です。
ちょっと手間ですが、同期させておきましょう。

参考にさせていただきました

『公式を見るのが一番早い』あるエライ人は言った。うん、その通りだと思います。
ドキュメントも整備されていて探しやすい。
Cloud Functions を使用した動的コンテンツの配信とマイクロサービスのホスティング  |  Firebase

最小構成のサンプルと詳細な説明がわかりやすく
なぜFunctions&hostingでnuxt.jsが動くのかを理解するのに非常に役に立ちました。
また、ビルドのスクリプトは目からうろこでございました。
nuxt.js + firebase (cloud functions) で最小構成SSR | 丸ノ内テックブログ

SPAとSSR、静的アプリケーションと動的アプリケーションの違いが参考になりました。
Nuxt.jsとFirebaseでSPA×SSR×PWA×サーバーレスを実現する - DMM inside

次回

FireStoreとつなげて読み書きができるようにします。

takeda-san.hatenablog.com