🔥

Firebase Javascript SDK v8→v9における進め方と注意事項

2021/09/20に公開約3,900字

はじめに

自分のアプリで使用しているFirebase Javascript SDKをv8からv9(modular方式)へ移行しました。その際につまづいた点がいくつかあったので、忘備録も兼ねて、進め方や注意点を整理しました。今回触れる機能は以下の通りとなります。

  • Authentication
  • Cloud Firestore
  • Cloud Storage
  • Cloud Functions

ModularCompatの説明については公式のcompatライブラリについてを参照ください。

方針

基本的には公式のアップグレードガイドに従い進めることができます。

https://firebase.google.com/docs/web/modular-upgrade
公式にも記載されていますが、compat(v8と互換)と共存させながら段階的にmodularへ移行することをお勧めします。以下のように両方の方式でFirebaseを初期化しておくことで、機能ごとに徐々に移行を進めることができます。
initializeApp.ts
import firebase from "firebase/compat/app"
import { initializeApp } from "firebase/app"

// compat(v8互換)方式の初期化
firebase.initializeApp({ /* config */ });
// modular方式の初期化
const firebaseApp = initializeApp({ /* config */ });

export default firebaseApp;

たとえば認証はmodularへ移行する一方で・・・

auth.ts
import firebaseApp from './initializeApp';
import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => {
  // Check for user status
});

Firestoreはv8互換のままとしたい(既存の書き方を変更しない)

firestore.ts
import firebase from "firebase/compat/app"
import "firebase/compat/firestore"

const db = firebase.firestore();
// 以降、既存のコードのまま
・・・

といった進め方をすることができます。
(自分の場合は一旦全てをcompatに置き換えて、一つずつmodularに移行しました)

Modularへの置き換え

各機能を順にmodularに置き換えていきます

Authentication / Cloud Functions

公式(Authentication/ Cloud Functions)のページにmodularスタイルの記法が載っており、こちらを参考に書き直していくことで比較的スムーズに移行を進めることができます。

AuthenticationやCloud FunctionsについてはFireStoreなどと比べて比較的単純な置き換えで済むため、modularへの移行の手始めとするのが良いかと思います。

FireStore

こちらも公式のFireStoreに従い、各メソッドを修正することでスムーズに進めることができます。但し、Firestoreの場合は長いドットチェーンをmodularに変更しないといけないケースがあるため、以下のように適切なヘルパー関数を使って読みやすくすることをお勧めします。

// ヘルパー:RoomのサブコレクションであるMessagesコレクションを取得する
const getMessageCollection = (roomId: string) => 
collection(doc(collection(firestore, 'rooms'), roomId), 'messages')
...
// 呼び出し側
addDoc(
        getMessageCollection(roomId),
        newMessageData
    ).then((result) => {
        //省略
    })

注意事項

existsプロパティを用いて以下のような処理を行なっている場合に少し注意が必要です

移行前
docRef.get().then((doc) => {
    if (doc.exists) { // ←v8でこの処理を記述をしている場合は注意
        // ドキュメントが存在する場合の処理
    } else {
        // ドキュメントが存在しない場合の処理
        console.log("No such document!");
    }
}).catch((error) => {
    console.log("Error getting document:", error);
});

existsプロパティはexists()メソッドに置き換わっており、then以降をv8の書き方で踏襲しようとした場合、ドキュメントの存在有無に関わらず、常にif文がtrueに評価されてしまう状況に陥ります(文法自体に問題はないためビルドエラーをすり抜けてしまう)。以下のように修正を行う必要があります

移行後
getDoc(doc(db, "cities", "SF")).then((doc) => {
    if (doc.exists()) { // ←気付きにくいが、メソッドに変更する必要あり
        // ドキュメントが存在する場合の処理
    } else {
        // ドキュメントが存在しない場合の処理
        console.log("No such document!");
    }
}).catch((error) => {
    console.log("Error getting document:", error);
});

(自分の場合はこの変更に気づかず、調査に結構な時間を費やしてしまいました・・・😢)

Storage

Storageは2021/9時点で日本語ドキュメントにv9の記載がありません。ただし、言語を英語にすることでv9の記述を確認することができます。

childがなくなっていたり、使い方が割と変更されていますが、基本的に公式ドキュメントに記載の通りに修正していけば問題はないはずです。

最後に

各機能の移行が無事済んだら、最後に初期化のcompatコードを削除します

- import firebase from "firebase/compat/app"
import { initializeApp } from "firebase/app"

- // compat(v8互換)方式の初期化
- firebase.initializeApp({ /* config */ });
// modular方式の初期化
const firebaseApp = initializeApp({ /* config */ });

export default firebaseApp;

modularに移行するには結構なリファクタリングが必要となりますが、今まで結構なサイズを占めていたFirebaseのバンドルサイズが大きく削減することができるので、苦労に見合うメリットはあるかと思います。

Discussion

ログインするとコメントできます