Firebase Javascript SDK v8→v9 のFireStore取扱書 in Nuxt × TypeScript

※この記事はQiita(Firebase Javascript SDK v8→v9 のCloud FireStore取扱書 in Nuxt × TypeScript)にも投稿されているものです。

はじめに

8月下旬、Firebase JavaScript SDK v9がリリースされました。

自分のポートフォリオサイトはFirebaseを利用していたのでv9へアップデートしたのですが、記法に互換性がなくなっていた影響で素直にアップデートするだけではダメでした。
そこで、v8, v9それぞれの記法の違いを簡単にまとめようと思います。

その際に遭遇した型エラーもこちらへ残します。

※ 今回はCloud FireStoreに触れます

対象者

  • Nuxt.jsでFirebaseのFireStoreを利用している方
  • Firebase Javascript SDK v8 → v9にアップデートしたい方
  • FireStoreに型を付けたい方
  • 🐭 !!🐮 !!🐯 🐰 🐲 🐍 🐴 🐏 🐵 🐔 🐶 🐗

筆者の環境

  • Node.js (14.18.0)
  • yarn (1.22.4)
  • Firebase (9.1.3)
  • TypeScript (4.2.4)
  • VScode(1.60.0)
  • Nuxtは2系です

まずはFirebase初期化部分から

■ v8 SDK

Firebase JavaScript SDK v8の詳しい導入方法はこちらに記載されています。
公式が「v9 SDK を使用することを強くおすすめします。」と言っているので特別な理由がない限りは、v9を利用した方がいいみたいです。

// v8
import firebase from 'firebase/app';

if (!firebase.apps.length) {
  firebase.initializeApp({/* config */});
}

export default firebase;

上記補足として、以下のエラーが発生することがあったので

Firebase: Firebase App named '[DEFAULT]' already exists

対処として

if (!firebase.apps.length) { 
  ...
}

で囲ってエラー回避していました。


■ v9 SDK

import 周りが変わっています。
利用するやつのみ import することによってコンパイル時のアプリサイズを削減できるのが売りらしいです。

当時のリリースノート

📦新しいモジュラーJavaScriptSDKは、アプリバンドルを最大80%小さくすることができますJavaScript SDKのバージョン9は、バンドラーが未使用のコードを排除し、JavaScriptバンドルのFirebaseライブラリコードを最大80%削減できるモジュールファーストフォーマットを採用しています。

configに型が用意されていたので早速使っています。

// 必要なもののみimport
import { FirebaseOptions, getApps, getApp, initializeApp, FirebaseApp } from 'firebase/app';

const firebaseConfig: FirebaseOptions = {/* config */};

const firebase: FirebaseApp = !getApps().length ? initializeApp(firebaseConfig) : getApp();

export default firebase;

v8の時に発生したエラーはv9では !getApps().length を利用した分岐で回避するようにしました。

Cloud FireStoreを利用する側

公式ドキュメント

ここでは、データ取得, データ追加周り v8 と v9 の時の記法をそれぞれご紹介します。
必要な部分のみ抜粋しています。

■ データ取得

はじめにデータ取得の実装周りです。

取得部分は今回 created 内に書いていますが、あくまで記事用なので created 内に書く特別な意図はありません。

□ v8 SDK

firebase/firestore はまるまるimportしています。

// v8
<script lang="ts">
import firebase from '~/plugins/firebase';
import 'firebase/firestore';

export default Vue.extend({
  created () {
    const db: firebase.firestore.Firestore = firebase.firestore(); // Firestore のインスタンスを初期化

    if (!db) { return; }
    db.collection('hoge').get()
      .then((querySnapshot) => {
        querySnapshot.forEach((doc: firebase.firestore.QueryDocumentSnapshot) => {
         ...
        });
      });
  }
})

□ v9 SDK

firestoreは必要な部分のみimportするようになりました。
記法が変わり、 getDocs という取得用の新しい関数が登場しました。

また、型定義も変わっています。

// v9
<script lang="ts">
import firebase from '~/plugins/firebase';
import { Firestore, getFirestore, getDocs, collection, QueryDocumentSnapshot } from 'firebase/firestore';

export default Vue.extend({
  created () {
    const db: Firestore = getFirestore(firebaseApp); // Firestore のインスタンスを初期化

    if (!db) { return; }
    getDocs(collection(db, 'hoge'))
      .then((querySnapshot) => {
        querySnapshot.forEach((doc: QueryDocumentSnapshot) => {
        ...
        });
      });
  }
})

■ データ追加

続いてデータ追加の実装周りです。

□ v8 SDK

// v8
<script lang="ts">
import firebase from '~/plugins/firebase';
import 'firebase/firestore';

export default Vue.extend({
  methods: {
    addData () {
      const db: Firestore = getFirestore(firebaseApp);
      db.collection("hoge").add({
          name: "deren",
          country: "Japan"
      });
    }
  }
})

□ v9 SDK

こちらも記法が変わり、 addDoc というデータ追加用の新しい関数が登場しました。

// v9
<script lang="ts">
import firebase from '~/plugins/firebase';
import { Firestore, getFirestore, addDoc, collection } from 'firebase/firestore';

export default Vue.extend({
  methods: {
    addData () {
      const db: Firestore = getFirestore(firebaseApp);
      await addDoc(collection(db, 'hoge'), {
        name: 'deren',
        country: 'Japan'
      });
    }
  }
})

v8 → v9にアップデートした際の型エラー

互換性がなくなった影響で、v8 → v9 へアップデートした際に型エラーがちらほら発生しました。


以下に記載する私が遭遇したエラーはこの記事で解消できるものなので、似たようなエラーに遭遇してしまったらこの記事を参考にしていただけると嬉しいです。

■ Cannot find namespace ‘firebase’

Cannot find namespace ‘firebase’
// v8
const db: firebase.firestore.Firestore = firebase.firestore(); 

// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// v9
const db: Firestore = getFirestore(firebaseApp);

※ 型は firebase/firestore から importしてください


■ Property ‘firestore’ does not exist on type ‘FirebaseApp’

Property ‘firestore’ does not exist on type ‘FirebaseApp’
// v8
const db: firebase.firestore.Firestore = firebase.firestore(); 

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// v9
const db: Firestore = getFirestore(firebaseApp);

※ 型は firebase/firestore から importしてください


■ Parameter ‘querySnapshot’ implicitly has an ‘any’ type.

rameter ‘querySnapshot’ implicitly has an ‘any’ type.
// v8
      await this.db.collection('hoge').get()
        .then((querySnapshot) => {
          querySnapshot.forEach((doc: firebase.firestore.QueryDocumentSnapshot) => {
            ...
          });
        })

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// v9
      await getDocs(collection(this.db, 'hoge'))
        .then((querySnapshot) => {
          querySnapshot.forEach((doc: QueryDocumentSnapshot) => {
            ...
          });
        })

※ 型は firebase/firestore から importしてください

参考