koke TechBlog

主にフロントエンド関係で学んだことを書いていきます。

2021/4/8

Next.js✕Firestoreまとめ ~クライアントサイド・サーバーサイドからのデータの入出力~

Next.jsでFirebase Cloud Firestore(以下Firestoreと書く)への、データの読み込み・書き込みを行う方法をまとめました。

前提

Firebaseとは

Firebaseはバックエンドの機能を提供するBaaS(backend as a service)の一つです。具体的にはFirestore、Firebase Storageなどのストレージサービス、Firebase Authenticationという認証サービス、ホスティングサービスなどの機能を提供しています。FirebaseにはSparkプランという無料プランが有り、小規模のブログくらいならこのプラン上で作成することができます。Sparkプランの詳しい内容は公式ドキュメントを参照してください。

Firebaseへの2通りのアクセスの仕方

Next.jsではクライアントサイドで動くコードと、API RouteやgetStaticProps内などのサーバーサイドで動くコードを書くことができます。Firebaseではサーバー(Node.js)用のFirebase Admin SDK、ウェブ用のFirebase SDKがそれぞれ提供されているので、使用環境に合わせて2つを使い分ける必要があります。

Firebase SDKを用いる場合は、後述するルールによってアクセスの権限を制御する必要がありますが、Firebase Admin SDKではルールに縛られず完全な管理者権限で読み書きを行うことができます。

Firebase側の準備

Firebaseでのプロジェクトの作成、テストデータの設置を行います。

プロジェクトの作成

  • https://console.firebase.google.com/ にアクセスして、アカウントの作成またはログインを行ってください。
  • 「プロジェクトを追加」を選択
  • プロジェクトの名前を入力。この際、ユニークなプロジェクトIDが割り当てられます。
  • Google Analiticsはご自由に。

プロジェクトを作成することが出来ると以下のような画面になると思います。

なお、このページのことをコンソール、Firebaseコンソールと呼ぶらしいです。コンソールというと黒い画面に文字を打ち込むイメージだったので、公式ドキュメントを読む際に混乱することがありました。

画面左端のアイコンをクリックすることでFirestore, Storage, Authenticationなどを設定する画面に移行します。

テストデータの設置

Firestoreにテストデータを設置していきます。

左端のアイコンよりFirestoreをクリック。

「データベースの作成」をクリック 画像のようなモーダルウィンドウにてモードを選択します。今回はクライアントサイドからもアクセスするために「テストモードで開始する」を選択。 次にロケーションの選択です。このロケーションは一旦設定すると変更することができないので、どうしても変える必要がある場合はプロジェクトを作り直すことになってしまいます。日本国内のロケーションは「asia-northeast1: 東京」、「asia-northeast2: 大阪」があるのでどちらかを選択。他の地域はこちらのページより確認することができます。

設定が完了すると以下のような画面になります。

「コレクションを開始」をクリックし、テストデータを作成していきます。今回は「users」コレクションを作成し、その中に書くユーザーの情報を記したドキュメントを格納していきましょう。 まずは最初のドキュメントとして画像のようにドキュメントIDを自動設定し、string型のnameとnumber型のageをフィールドに持つデータを作成します。この記事では扱いませんが、string、number以外にもboolean、array、timestampなど様々なタイプを選択することが出来るので、公式ドキュメントなどを参考に使い分けましょう。

「ドキュメントを追加」よりname、ageをフィールドに持つデータをもう1つ追加しておきましょう。

以上でFirestoreへのテストデータの設置は終了です。

また、Firestoreではドキュメント内でコレクションを作成することができ(このコレクションをサブコレクションと呼ぶ)、users/user_doc1/message/message_doc1/のようなネスト構造も作成できます。しかしサブコレクションを使う際は、ドキュメントを削除してもそのサブコレクションは削除されないことに注意が必要です。

users/user_doc1/message/message_doc1/の例で言うと、user_doc1というドキュメントを削除したとしても、messageやmessage_doc1は残る可能性があります。詳しくはデータモデル > サブコレクションデータの削除 > コレクションを削除するを参照。

SDKの準備

firebaseにアクセスするためにNext.js側でSDKの準備を行います。

クライアントサイド(Firebase SDK)とサーバーサイド(Firebase Admin SDK)で準備の手順が異なるので、使いたい方の手順に沿って準備を行ってください。

クライアントサイド(Firebase SDK)を使うための準備

まずはNext.jsのプロジェクトを作成します。すでにプロジェクトを用意してある場合は飛ばしてください。

npx create-next-app firebase-test
# または
yarn create next-app firebase-test

Firebase SDKの追加を行います。

cd firebase-test
# Firebase SDKを追加します。
npm install firebase

次に、Firebase SDKの初期化を行います。

Firebase consoleのホームよりwebの追加を選択

アプリの名前を入力します

画像のようなコードが表示されるので、var firebaseConfig = {...}をコピーしておきます。

プロジェクト直下にfirebaseフォルダを作成し、その中にfirebase_init.jsを作成します。firebase-test/firebase/firebase_init.js

firebase_init.jsには以下のコードを貼り付けます。var firebaseConfigにはコピーしたものを貼り付けてください。 firebase.initializeApp()が複数回実行されるとエラーが出るのでtry-catchで囲んでおきます。

import firebase from "firebase";

var firebaseConfig = {
	// your configs.
};

try {
  firebase.initializeApp(firebaseConfig);
} catch {}

export const db = firebase.firestore();

これで、Firebase SDKを使う準備は完了です。

なお、firebaseConfig内のapiKeyなどの値は公開情報です。なので、githubに上げる際も環境変数などを用いて秘匿する必要はないです。読み書きの権限などはセキュリティルールで管理しなければいけません。本番環境にアップする前に必ずセキュリティルールの設定を行ってください。

以下の記事でAPI Keyについて書かれているので、気になる方は参考に。

サーバーサイド(firebase-admin)を使うための準備

Next.jsのプロジェクトを作成します。すでにプロジェクトを用意してある場合は飛ばしてください。

npx create-next-app firebase-test
# または
yarn create next-app firebase-test

Firebase Admin SDKの追加を行います。

cd firebase-test
# Firebase Admin SDKを追加します。
npm install firebase-admin

次に、Firebase Admin SDKの初期化を行います。

firebaseコンソールで「プロジェクトの設定」→「サービスアカウント」にアクセス。

「Admin SDK 構成スニペット」をNode.jsにして「新しい秘密鍵の生成」を行う。 無事生成されるとJSONファイルがダウンロードされます。

Next.jsのプロジェクトのルートにfirebaseというフォルダを作成し、その中にこのJSONファイルを設置しましょう。 firebase-test/firebase/XXXX-firebase-adminsdk-XXXX.json

鍵生成時に注意があったように、このファイルは機密情報なので、githubなどに公開してはいけません。gitignoreに追加しておきましょう。

firebaseフォルダにfirebase_init.jsを作成します。 firebase-test/firebase/firebase_init.js

そこに以下のコードを貼り付け、初期化を行います。

*your_serviceAccountKey_file_nameにはダウンロードしたJSONファイルのファイル名を入れてください。

var admin = require("firebase-admin");

// ダウンロードしたJSONファイルをインポート。
var serviceAccount = require("./your_serviceAccountKey_file_name.json");

try {
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
  });
} catch {}

export const db = admin.firestore();

Firebase Admin SDKの準備は以上です。

データを読み取る

firestoreに設置したデータを読み取っていきます。記事のコードはpage/index.jsでデータを読み取る前提で書かれているので、他のファイルから読み込む場合はimportの引用元などを適時変更してください。

データの読み取りは以下の手順で行われます。

  • ドキュメント、ドキュメントのリストまたはコレクションへの参照を作成。
  • 参照のgetメソッドを実行しドキュメントの値を獲得。

1. ドキュメント、ドキュメントのリストまたはコレクションへの参照を作成。

まずは、データを読み取りたいファイルでfirebase_init.jsからdbをimportします。

import { db } from "../firebase/firebase_init";

ここから、データを読み取るための処理を書いていきますが、クライアントサイドのSDK(Firebase SDK)を使う場合はdefault exportするコンポーネント内などの、クライアントサイドで動く部分に、サーバーサイドのSDK(Firebase Admin SDK)を使う場合は、getStaticPropsgetServerSidePropsなどのサーバーサイドで動く部分にコードを書くことに注意をしてください。

import { db } from "../firebase/firebase_init";

export default function Home(){

	/* クライアントサイドのSDKを使える */

	return <div></div>
}

export function getStaticProps(){

	/* サーバーサイドのSDKを使える */

	return {
		props: {}
	}
}

dbのcollectionメソッドでコレクション名を指定することでコレクションへの参照を、コレクションへの参照にdocメソッドでドキュメントのidを指定することでドキュメントへの参照を獲得することることが出来ます。

// usersコレクションへの参照
const collection_ref = db.collection("users")

// usersコレクションのuser_1ドキュメントへの参照
const doc_ref = db.collection("users").doc("user1")

whereメソッドでクエリを作成することが出来ます。whereメソッドは3つの引数を取り、フィールド、クエリ演算子、値の順に指定することが出来ます。クエリ演算子は、等しい"=="、値以下"≤"など様々な条件を指定することが出来ます。詳しくは公式ドキュメントを参照してください。

// usersコレクションの中の、ageが20以下のドキュメントへの参照
const young_ref = db.collection("users").where("age", "<=", 20)

orderByメソッドでドキュメントの並べ替えを行うことが出来ます。ただし、orderByメソッドで並び替えたフィールド以外のフィールドで、whereメソッドの範囲比較((<, ≤, >, ≥)を行うことは出来ないので注意が必要です。

// usersコレクションのドキュメントを年齢順に並べた参照
// 昇順
const younger_ref = db.collection("users").orderBy("age")
// 降順
const older_ref = db.collection("users").orderBy("age", "desc")

limitメソッドを使うことで取得するドキュメントの数を制限することが出来ます。

// usersコレクションのドキュメントを年齢順に並べて、始めの3つのドキュメントを取得
const three_ref = db.collection("users").orderBy("age").limit(3)

以上のメソッドを組み合わせてコレクションやドキュメントへの参照を作成します。

// usersコレクションのうち20歳以上のデータを若い順に10個取得する。
const ten_ref = db.collection("users").where("age", >=, 20).orderBy("age").limit(10)

2. 参照のgetメソッドを実行しドキュメントの値を獲得。

コレクションやドキュメントへの参照でgetメソッドを実行すると、コレクションやドキュメントの内容を獲得できます。getメソッドは非同期関数なことに注意をしてください。

// usersコレクションへの参照を作成。
const ref = db.collection("users");
// usersの内容を獲得。非同期処理に注意。
const users_snapshot = ref.get();
// ドキュメントの内容を出力。
users_snapshot.then((users) =>
  users.forEach((user) => {
    console.log(
      `id: ${user.id}, name: ${user.data().name}, age: ${user.data().age}`
    );
  })
);

ファイルを保存し、npm run devでサーバーを立てlocalhost:3000/にアクセスすると無事ドキュメントの内容が出力されました。

データを書き込む

ドキュメント新規作成の場合はsetかaddメソッドを使って行います。

setメソッドを使う場合は、作成したいドキュメントへの参照にsetメソッドを使用します。addメソッドを使う場合は、ドキュメントを追加したいコレクションへの参照に対して使用することで、idが自動的に割り振られたドキュメントを作成します。

setメソッドで元からあるドキュメントと同じidを指定すると、元からあるドキュメントを上書きします。

ドキュメントへの参照にupdateメソッドを使用するとドキュメント全体を上書きせず、一部のフィールドのみを更新することが出来ます。

const doc_data = {
    age: 40,
    name: "saburo"
  }
const doc_data2 = {
		age: 45,
		name: "siro"
	}
  // usersコレクションにidがuser3のドキュメントを追加。
  db.collection("users").doc("user3").set(doc_data);
  // users/user3を上書き。
  db.collection("users").doc("user3").set(doc_data2);
  // users/user3のageを50に更新
  db.collection("users").doc("user3").update({age: 50})

  // usersコレクションにid自動割当でドキュメントを追加
  db.collection("users").add(doc_data);

ファイルを保存し、npm run devでサーバーを立てlocalhost:3000/にアクセスします。その後、FirebaseコンソールよりFirestoreを確認すると無事、ドキュメントが新規作成、更新されました。

おわりに

以上のような手順でFirestoreにデータを読み書きすることが出来ます。Firestoreには本記事で紹介した以外にも様々な機能があり、無料プランが充実しているので、気になった方はぜひ使ってみてください。また、FirebaseにはFirestore以外にも画像などを保存できるFirebase Storegeや認証をしてくれるFirebase Authenticationなどもあるので使いたいサービスがあれば調べてみてください。

ここまでお読みいただき、ありがとうございました。

;