Next.js(ほぼTypeScript)でFireStore使ってみた

# 導入
今回はフロントエンド開発において、FirebaseのFireStoreのCRUD操作を行ってみたので、記事にしました。

# 手順
## セットアップ

### 1. Firebase プロジェクトのセットアップ

以下のリンクからFirebaseコンソールにログインし、新しいプロジェクトを作成します。

https://console.firebase.google.com/u/3/

プロジェクト作成後、Firestore を有効にします。

### 2. Firebase SDK のセットアップ

Next.jsプロジェクトに対して`Firebase SDK` をインストールします。
次のコマンドを実行することで、インストールされます。

```bash
npm install firebase
```

### 3. Firebase の設定
Next.jsプロジェクト内で、 `Firebase SDK` を初期化し、設定を行います。
今回は、`firebaseConfig` を設定します。

```javascript
import firebase from 'firebase/app';
import 'firebase/firestore';

const firebaseConfig = {
    apiKey: "API_KEY",
    authDomain: "AUTH_DOMAIN",
    projectId: "PROJECT_ID",
    storageBucket: "STORAGE_BUCKET",
    messagingSenderId: "MESSAGING_SENDER_ID",
    appId: "APP_ID",
    databaseUrl: "DATABASE_URL"
};

if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

const db = firebase.firestore();

export { db };
```

:::note info
上記ソースコードの"API_KEY"等の値はFirebaseの設定画面から確認してください。
<details><summary>確認方法</summary>

**databaseUrl以外**
Firebaseのプロジェクトの設定を開き、「全般」を開きます。

![スクリーンショット 2024-06-26 22.37.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/37f72c7f-9e29-7a59-f6e4-a28383bc6e2d.png)

下にスクロールしていくと、各パラメータがあるので、これらをコピーしてください。

![スクリーンショット 2024-06-26 22.38.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/ce1b6c2e-8b18-2cc8-6d87-1fadd2908538.png)

**databaseUrl**
Firebaseのプロジェクトの設定を開き、「サービスアカウント」を開きます。
緑で隠された場所にURLがあるので、それをコピーしてください。

![スクリーンショット 2024-06-26 22.41.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/e6b8c928-71c4-d3ec-a9e7-2332511e1644.png)

</details>

:::

:::note alert
"API_KEY"等は流出すると悪用される可能性があるので、環境変数として、[.env]ファイルなどで管理するようにしましょう。本記事ではその対応を割愛しています。
:::

## CRUD 操作の実装
Firestore でのデータの追加、取得、更新、削除の操作を実装します。

- データの追加
```ts
async function addData(data: HogeData) {
    await db.collection('collectionName').add(data);
}
```

- データの取得
```ts
async function fetchData() {
    const snapshot = await db.collection('collectionName').get();
    return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
}
```

- データの更新
```ts
async function updateData(id: number, newData: HogeData) {
    await db.collection('collectionName').doc(id).update(newData);
}
```

- データの削除
```ts
async function deleteData(id: number) {
    await db.collection('collectionName').doc(id).delete();
}
```

#### 応用(データの変化を取得する方法)
FireStoreはNoSQLであり、応答が早いので、チャットアプリケーションとして利用することができます。
チャットでは、誰かがFireStoreのデータを追加(チャットメッセージの追加)をした場合、すぐにその情報が他のユーザにも反映される必要があります。
そういったユースケースの場合、以下の実装が適切です。

```ts
private updateStockItems = () => {
    const q = query(collection(getFirestore(), 'collectionName'))
    const unsubscribe = onSnapshot(q, async (snapshot) => {
        const changes = snapshot.docChanges()
        for (const change of changes) {
            // データが追加された場合の処理
            if (change.type === 'added') {
                const changeData = change.doc.data()
            }
            // データが変更された場合の処理
            if (change.type === 'modified') {

            }
            // データが削除された場合の処理
            if (change.type === "removed") {

            }
        }
    }, (error) => {
        console.log(error)
    })
}
```

このように実装することで、チャットが追加(更新・削除)された際にすぐに画面にそれを反映することが可能になります。


# 終わりに
フロントエンドから直接NoSQLであるFireStoreにアクセスする方法を解説しました。
バックエンドの実装が不要なので、簡単なWebアプリケーションを作成したい方はぜひ参考にしていただけたらと思います。