Nuxt.jsとFirebase RealtimeDatabase CloudFirestore でBaaS開発入門

福岡から世界中の"むずかしい"を簡単にする株式会社[diffeasy](https://diffeasy.com)CTOの西[@_takeshi_24](https://twitter.com/_takeshi_24)です。

この記事はアドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」の5日目の記事です。

この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！

## はじめに
こちらの記事は、「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして連載していますので、過去の記事もご覧ください。

- [Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発 - 準備編](https://qiita.com/_takeshi_24/items/c26e4a664e27b652d9a6)
- [Nuxt.jsとFirebaseAuthentication/FirebaseUIで認証](https://qiita.com/_takeshi_24/items/4ea05d5564a5d5ca9825)

##RealtimeDatabaseとCloudFirestore
FirebaseのデータベースにはRealtimeDatabaseとCloudFirestoreの２種類があります。
[データベースを選択: Cloud Firestore または Realtime Database](https://firebase.google.com/docs/database/rtdb-vs-firestore?hl=ja)にも解説がありますが、それぞれ特徴を簡単に説明すると、

- RealtimeDatabase:レイテンシが低く、シンプルなリアルタイム処理に向いている。
- CloudFirestore:RealtimeDatabaseよりもクエリが柔軟で高速。

といった感じです。

##Nuxt.jsでRealtimeDatabase
以前の記事では[vuexfire](https://github.com/posva/vuexfire/graphs/contributors)というパッケージを使っていましたが、最近はあまりメンテナンスされていないようです。
<img width="735" alt="スクリーンショット 2019-12-03 22.50.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/ec6e0c56-c5c6-67dd-ef51-f5844d116bd3.png">

vuexfireを使わなくても、簡単にNuxt.jsからFirebaseのRealtimeDatabase、Firestoreを扱えますので、今回はその方法を説明します。

まずはFirebaseのRealtimeDatabaseにデータを書き込み、リアルタイムで表示します。

messagesというコレクションにmessageTextという値を追加します。

1./pages/messages/index.vueを以下の内容で作成します。

```vuejs:/pages/messages/index.vue
<template>
  <section class="container">
    <v-row>
      <v-col>
        <v-text-field v-model="messageText"></v-text-field>
      </v-col>
      <v-col>
        <v-btn @click="addMessage">投稿</v-btn>
      </v-col>
    </v-row>
    <v-row v-for="(message, key, index) in messages" :key="index">
      <v-col>
        <p>
          {{ message.messageText }}
        </p>
      </v-col>
    </v-row>
  </section>
</template>
<script>
import firebase from "firebase";

export default {
  data() {
    return {
      messages: [],
      messageText: ""
    };
  },
  mounted() {
    this.initMessages();
  },
  methods: {
    initMessages() {
      firebase
        .database()
        .ref("messages")
        .on("child_added", snapshot => {
          this.messages.unshift(snapshot.val());
        });
    },
    addMessage() {
      firebase
        .database()
        .ref("messages")
        .push({ messageText: this.messageText });
      this.messageText = "";
    }
  }
};
</script>

```
2.```yarn run dev```で起動して、テキストにメッセージを入力し、「投稿」ボタンをクリックします。

3.```Permission denied```のエラーが返ってきます。RealtimeDatabaseに対する書き込み権限がないためです。ログインユーザーのみ、RealtimeDatabaseの読み込み書き込みができるように、```firebase.json```と同じ場所にある```database.rules.json```ファイルを修正します。

```json:database.rules.json
{
  "rules": {
    "messages": {
      ".read": "auth != null",
      ".write": "auth != null"
    }
  }
}
```

4.DBルールをデプロイします。

```terminal
$ firebase deploy --only database
```

5.もう一度テキストにメッセージを入力し、「投稿」ボタンをクリックします。今度は投稿できて、投稿した内容がリアルタイムで表示されました。
<img width="892" alt="スクリーンショット 2019-11-30 17.28.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/a5182e73-15cb-32c8-b0e5-c4d3e2abcfcf.png">

##Nuxt.jsでCloudFirestore
次に、Firestoreのusersコレクションにログイン中のユーザー情報を登録処理を作ります。

1./pages/current_user/index.vueを以下の内容で作成する。

```vuejs:/pages/current_user/index.v
<template>
  <section class="container">
    <v-row>
      <v-col>
        <v-text-field v-model="familyName" placeholder="姓"></v-text-field>
      </v-col>
      <v-col>
        <v-text-field v-model="firstName" placeholder="名"></v-text-field>
      </v-col>
      <v-col>
        <v-btn @click="updateUser">更新</v-btn>
      </v-col>
    </v-row>
  </section>
</template>
<script>
import firebase from "firebase";
const db = firebase.firestore();

export default {
  data() {
    return {
      uid: "",
      firstName: "",
      familyName: ""
    };
  },
  created() {
    firebase.auth().onAuthStateChanged(user => {
      this.uid = user.uid;
      this.getUser();
    });
  },
  methods: {
    async getUser() {
      const userRef = db.collection("users").doc(this.uid);
      const userDoc = await userRef.get();
      if (userDoc.exists) {
        this.firstName = userDoc.data().firstName;
        this.familyName = userDoc.data().familyName;
      }
    },
    updateUser() {
      db.collection("users")
        .doc(this.uid)
        .set({
          firstName: this.firstName,
          familyName: this.familyName
        });
    }
  }
};
</script>
```
firebaseの認証情報が非同期で取得されますので、firebase.auth().onAuthStateChangedでログインユーザーのuidを取得します。

getUser()でログインユーザーの情報を取得して、updateUser()でログインユーザーの情報を更新しています。

通常のサービスであれば、セッション情報などを使って、サーバーサイドでUIDを取得すれば安全ですが、BaaSのFirebaseをNuxt.jsから呼ぶ場合、クライアントからUIDをサーバーに渡す必要があります。

クライアント側でUIDを渡しているので、当然改竄しようと思えば、改竄可能です。
他人のUIDのデータを改竄されたら問題なので、Firestore側でデータ更新ルールを使ってデータを保護する必要があります。

2.```firebase.json```と同じ場所にある```firestore.rules```ファイルを修正します。

```
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, update, delete: if request.auth.uid == userId;
      allow create: if request.auth.uid != null;
    }
  }
}
```

usersコレクションのuserIdとログインユーザーのUIDが一致していれば、読み込み、更新、削除可能で、ログイン中であれば登録できるルールに設定します。

なお、今回getUser()でcreated時にusersコレクションのデータを取得していますが、リアルタイムで取得するには、[onSnapshot](https://firebase.google.com/docs/firestore/query-data/listen?hl=ja)を利用すると、リアルタイムで取得できます。

データの更新処理には、データ一貫性を保持する必要があり、トランザクションは必須です。
Firebaseのトランザクションについてはまた今後の記事で紹介します。

##最後に
この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
続きはアドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」に掲載していきます。

Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！

[#advent_24](https://twitter.com/hashtag/advent_24)のハッシュタグでフィードバックいただけると嬉しいです！