Nuxt3 での Firestore の利用

## はじめに

`Nuxt3` で `Firestore` を使う際に、いろいろと調べましたがスッキリとまとめられている記事がなかったのでまとめます。

前提として、 `Firebase` のプロジェクト作成などは完了している状態とします。

`Nuxt3` プロジェクトの構築などは、 [以前の記事](https://qiita.com/kurararara/items/ebe561016e4b486b7673) などを参考にしてください。



## Firebase のインストール

### インストール

`Firestore` を利用するために、 `Firebase` をインストールしていきます。

```shell
npm install firebase
```



### 初期設定

`plugins/firebase.client.ts` を作成します。
（ `plugins` フォルダがない場合は、フォルダも作成）

作成したファイルに次のように記述します。

```typescript:plugins/firebase.client.ts
import { initializeApp } from 'firebase/app'
import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()

  const firebaseConfig = {
    
    // この部分に Firebase のプロジェクトの内容を記載する（下記参照）
    
  }

  const app = initializeApp(firebaseConfig)
})

```

![firebase-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135956/060d84fc-1792-f0b4-1e1c-00cc7feaba8d.png)


なお、 `Nuxt2` であれば `plugins` フォルダに作成したファイルを読み込むために、 `nuxt.config.ts` の設定が必要でしたが、 `Nuxt3` では自動で読み込みされます。
（便利！！）



### Composable の作成

設定は終わっているので、 `Firestore` の利用はいつでもできるのですが、各ページや、各コンポーネントで取得処理や登録処理を書くのは煩雑だと思います。

`Nuxt3` であれば `Composable` を利用するのがきれいだと（個人的には）思います。

#### Firestore にアクセスするための Composable の作成

`composables/useFirestore.ts` を作成し、次のように記載します。
（ `composables` フォルダがない場合は、フォルダも作成）



```typescript:composables/useFirestore.ts
import { getFirestore } from 'firebase/firestore'

/**
 * Firestore へのアクセス
 */
export const useFirestore = () => {
  const db = getFirestore()

  return {
    db,
  }
}
```



`useFirestore` はプロジェクトのどこからでもアクセス可能な `Composable` になりました。
現時点では、 `Firestore` インスタンスの初期化をする処理しか入っていません。



#### コレクションからデータを取得する関数を追加

例えば、ユーザー情報を管理する `user` コレクションがあるとします。
タイプとしてはこんな感じで。

```typescript
/**
 * [Firestore] `user` コレクション用のタイプ
 */
export type User = {
  firstName: string
  lastName: string
  birthday: string
  email: string
}
```

`user` コレクションからメールアドレスをキーに検索する場合は次のように関数を追加します。

```diff_typescript
import {
  getFirestore,
+ collection,
+ query,
+ where,
+ getDocs,
} from 'firebase/firestore'
+import { User } from './types/firestore'

/**
 * Firestore へのアクセス
 */
export const useFirestore = () => {
  const db = getFirestore()

+ /**
+  * [Firestore] `user` コレクションをメールアドレスで検索し、 `User` オブジェクトを返す
+  *
+  * @param {string | null} email メールアドレス
+  * @returns {Promise<User | null>} `User` オブジェクト
+  */
+ const getUser = async (email: string | null): Promise<User | null> => {
+   if (!email) {
+     return null
+   }
+
+   const q = query(collection(db, 'user'), where('email', '==', email))
+   const querySnapshot = await getDocs(q)
+   const users = querySnapshot.docs.map((doc) => {
+     return doc.data() as User
+   })
+
+   if (users.length) {
+     return users[0]
+   }
+   return null
+ }

  return {
    db,
+   getUser,
  }
}
```



### Composable の利用

メールアドレスからユーザー情報を検索する `Composable` ができましたので、呼び出してみましょう。

```vue
<script setup lang="ts">

const email = ref()
const user = ref<User>()

onMounted(async () => {
  
  // Firestore へアクセスする場合は、最低でも mount 後にしましょう
  user.value = await useFirestore().getUser(user.value.email)
})
    
</script>
```

`Composable` を利用してスッキリと記載ができました。



### useFirestore を充実させる

ユーザー情報を登録したい、といったように機能を追加したい場合は、 `useFirestore` に関数を追加していきましょう。



```diff_typescript
import {
  getFirestore,
  collection,
+ addDoc,
  query,
  where,
  getDocs,
} from 'firebase/firestore'
import { User } from './types/firestore'

/**
 * Firestore へのアクセス
 */
export const useFirestore = () => {
  const db = getFirestore()

  /**
   * [Firestore] `user` コレクションをメールアドレスで検索し、 `User` オブジェクトを返す
   *
   * @param {string | null} email メールアドレス
   * @returns {Promise<User | null>} `User` オブジェクト
   */
  const getUser = async (email: string | null): Promise<User | null> => {
    if (!email) {
      return null
    }

    const q = query(collection(db, 'user'), where('email', '==', email))
    const querySnapshot = await getDocs(q)
    const users = querySnapshot.docs.map((doc) => {
      return doc.data() as User
    })

    if (users.length) {
      return users[0]
    }
    return null
  }
  
+ /**
+  * [Firestore] `user` コレクションにドキュメントを登録する
+  *
+  * @param {User} user `User` オブジェクト
+  */
+ const addUser = async (user: User) => {
+   if (!user) {
+     return null
+   }
+
+    await addDoc(collection(db, 'user'), user)
+  }

  return {
    db,
    getUser,
+   addUser,
  }
}
```

あとは使うだけですね！

```typescript
await useFirestore().addUser(user.value)
```



## まとめ

`Nuxt3` での `Firestore` の利用についてまとめました。

ネットに断片的に情報はあるのですが、なかなかスッキリとまとまったものがなかったので、この記事をみて役に立つ人がいると嬉しいです！