LoginSignup
7
1

More than 3 years have passed since last update.

SPA(Nuxt.js) をFirebase上で動かすにあたって、開発進行中につまずいたこと

Last updated at Posted at 2020-12-24

はじめに

最近バックエンドのコードを書く機会が減り、代わりにFirebaseを利用する機会が本当に増えてきました。
本投稿はSPA(Nuxt.js)で実装したものをFirebase上で動かすにあたって、Hello Worldから毛が生えアプリが成長し複雑になっていく過程で、つまずいた体験の紹介になります。

1.動的なルーティングが動かなくて、つまずいた

Nuxt.jsでgenerateしてdistディレクトリをFirebase Hostingにdeployすれば、できたできたヤッターとなります。
ですが、動的なルーティング1は単純にそれだけだとFirebase Hosting上では動きません。
とりあえずサクッと対応したいという場合はhashモードにかえましょう。

nuxt.config.js
  router: {
    mode: 'hash',
  },

2.Functionsで作ったREST APIがローカルから呼べなくて、つまずいた

functions/index.js
const functions = require('firebase-functions');

exports.getSample = functions.https.onRequest(async (request, response) => {
  res.json({success: true, date: (new Date()).getTime() });
});

こんな感じで作成したFunctionsをdeployすれば
https://us-central1-[PROJCECT_ID].cloudfunctions.net/getSample
というURLのREST APIができます。ブラウザで試しにアクセスすると、正しくJSONが出力されることが確認できました。

それではこれをクライアントから呼び出してみますが、

クライアント呼び出し側(ダメな例)
return axios({
  method: 'GET',
  url: 'https://us-central1-[PROJECT_ID].cloudfunctions.net/getSample',
})

こうして、yarn dev からのhttp://localhost:3000 での動作確認なんてことをしちゃうとエラーになってしまいます。
これは単純なCORSによるエラーなので、対応としては

nuxt.config.js
proxy: {
  '/functions_api': {
    target: 'https://us-central1-[PROJECT_ID].cloudfunctions.net',
    pathRewrite: {
      '^/functions_api': '/',
    },
  },
},
クライアント呼び出し側(修正後)
return axios({
  method: 'GET',
  url: '/functions_api/getSample',
})

とすれば、http://localhost:3000 での動作確認もうまくいくようになります。
ちなみに上記proxyを変更すれば向け先をEmulatorで動かしているFunctionsにすることも可能ですね。
そんなこんなで開発は進行し、さらに修正して

クライアント呼び出し側(さらに修正後)
const currentUser = firebase.auth().currentUser
if (currentUser) {
  const token = await currentUser.getIdToken()
  return axios({
    method: 'GET',
    url: '/functions_api/getSample',
    headers: { Authorization: `Bearer ${token}` },
  })
}
functions/index.js(さらに修正後)
const admin = require('firebase-admin');
const functions = require('firebase-functions');

admin.initializeApp();

exports.getSample = functions.https.onRequest(async (request, response) => {
  const token = req.headers.authorization.split('Bearer ')[1];
  const decodedToken = await admin.auth().verifyIdToken(token);

  // decodedToken.uidを用いて適切なアクセス制限実行

  res.json({success: true, date: (new Date()).getTime() });
});

Firebase Authenticationを通過しているユーザのみアクセス可能にする、という実装に変えました。

3.Functionsで作ったREST APIが本番Hostingから呼べなくて、つまずいた

2の修正後に、generateしたものをFirebase Hostingにdeployして動作確認してみました。

なんということでしょう、またREST APIが呼び出せていません。ブラウザのデバッガーで確認すると

GET https://[PROJECT_ID].web.app/functions_api/getSample 404

こんなエラーが出ています。え?404?、、あーこのURLにアクセスしちゃうのね。。
ということで、firebase.jsonでrewritesを追記しました。

firebase.json
"hosting": {
  "public": "dist",
  "ignore": [
    "firebase.json",
    "**/.*",
    "**/node_modules/**"
  ],
  "rewrites": [
    {
      "source": "/functions_api/getSample",
      "function": "getSample"
    },
  },
},

これにて、本番Hostingでも正常にアクセスできるようになりました!ヤッター!

(おまけ)Functionsで作ったREST APIが重くて、つまずいた

せっかく上記2と3を乗り越えて作ったものの、やたらレスポンスが遅いのが気になってしまいました。
重くてもしょうがない性質な処理でもあったので、高速化を試みるのではなく、処理の最初の方で、res.json()によりレスポンスだけササッと返して、裏でゆーっくりと処理しておいてくんないかなと思ったものの、上手く行かず。。。
そこで、HTTPリクエストトリガーでの実行をやめ、Cloud Firestoreトリガーに変更して、クライアント側からはFunctionsへのキュー的な指示のためのデータをFirestoreにsetするというやり方に変えてみました。
すると、上記であった「Authenticationを通過しているユーザのみアクセス可能に」を考えなくてすむし、責任分界点がはっきりして開発効率悪くないし、これはこれで案外いいかもな〜って思ったりもしました。

おしまい

ではでは、皆様メリクリ〜


  1. pages配下に_id.vueのようなアンスコ始まりのファイルを設置して、this.$route.params.id で値が参照できるNuxtの機能。(参考:ファイルシステムに基づくルーティング) 

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1