Firebase Admin SDK v11 を用いた Firestore のデータ操作方法
投稿日: 2024/03/14
更新日: 2024/03/26
Firebase functionsを含めたサーバーサイドでFirestoreを操作する際、Firebase Admin SDKを使用します。しかし、API Referenceはありますがドキュメントは見当たりませんし、クライアントサイドからデータを操作するときに用いるFirebase Javascript SDKに比べて情報が少なかったので、一通りの操作方法をまとめます。
追記: 公式ドキュメントのサンプルコードをNode.js
に切り替えると、Firebase Admin SDKを使ったコードが見れました(気づかなかった)。せっかく書いたので公開しますが、公式ドキュメントにもコードのサンプルがありました。
前提
Firebase Admin SDKはセキュリティルールを無視しますので注意しましょう。
クライエント側のSDK(Firebase Javascript SDK)と同じような制約があります。それについて基本的に説明しないのでクライエント側のドキュメントを読んでください。
articlesコレクションがあるとします。
fieldはこんな感じです。
{ title: string userUid: string body: string public: boolean createdAt: timestamp updatedAt: timestamp }
保存されてるデータは以下のようなものとします。
[ { public: true, title: '1', body: '1', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 }, updatedAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 } }, { public: false, title: '2', body: '2', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412871, _nanoseconds: 89000000 }, updatedAt: Timestamp { _seconds: 1710412871, _nanoseconds: 89000000 } }, { public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } }, { public: true, title: "another user's 1", createdAt: Timestamp { _seconds: 1710416766, _nanoseconds: 516000000 }, body: "another user's 1", updatedAt: Timestamp { _seconds: 1710416784, _nanoseconds: 973000000 }, userUid: 'ADedsD44tdFGrfgerGFErgergRRR' } ]
データの読み取り
1つのドキュメントの取得
単にdoc
メソッドでuidを指定して取得できます。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const docRef = adminDb .collection("articles") .doc("R9szMVG0vsfd7iUx08Wq") const docSnap = await docRef.get() console.log(docSnap.data())
output
{ public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } }
複数のドキュメントの取得
where()
フィルタを省略してすべて取得することもできます。
where()
フィルタについては後でもう少し詳しく説明します。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .where("public", "==", true) const querySnap = await queryRef.get() console.log(querySnap.docs.map((doc) => doc.data()))
output
[ { public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } }, { public: true, title: '1', body: '1', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 }, updatedAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 } }, { public: true, title: "another user's 1", createdAt: Timestamp { _seconds: 1710416766, _nanoseconds: 516000000 }, body: "another user's 1", updatedAt: Timestamp { _seconds: 1710416784, _nanoseconds: 973000000 }, userUid: 'ADedsD44tdFGrfgerGFErgergRRR' } ]
単純なクエリで複数のドキュメントの取得
where()
メソッドはクライアント用のSDKと同じ比較演算子が使えます。
<
: より小さい<=
: 以下==
等しい>
より大きい>=
以上!=
等しくないarray-contains
array-contains-any
in
not-in
!=
クエリ句を使う際は多くのドキュメントが一致することが多いので、limit
を使うようにクライエントのドキュメントに書いてあります。というか!=
クエリ句を使わない場合もlimit
使いましょう。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .where("public", "==", true) .limit(1) const querySnap = await queryRef.get() console.log(querySnap.docs.map((doc) => doc.data()))
output
[ { public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } } ]
複合(AND)クエリで複数のドキュメントの取得
admin.firestore.Filter
にand()
メソッドがあります。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .where( admin.firestore.Filter.and( admin.firestore.Filter.where("public", "==", true), admin.firestore.Filter.where( "userUid", "==", "EVyT9uSk92ddwPeBMjElL5Qx6PCK" ) ) ) .limit(5) const querySnap = await queryRef.get() console.log(querySnap.docs.map((doc) => doc.data()))
output
[ { public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } }, { public: true, title: '1', body: '1', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 }, updatedAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 } } ]
複合(OR)クエリで複数のドキュメントの取得
admin.firestore.Filter
にor()
メソッドがあります。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .where( admin.firestore.Filter.or( admin.firestore.Filter.where("title", "==", "1"), admin.firestore.Filter.where("title", "==", "2") ) ) .limit(5) const querySnap = await queryRef.get() console.log(querySnap.docs.map((doc) => doc.data()))
output
[ { public: true, title: '1', body: '1', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 }, updatedAt: Timestamp { _seconds: 1710412862, _nanoseconds: 740000000 } }, { public: false, title: '2', body: '2', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412871, _nanoseconds: 89000000 }, updatedAt: Timestamp { _seconds: 1710412871, _nanoseconds: 89000000 } } ]
データを並べ替えたり制限する
orderBy()
やlimit()
もクライエントと同じように使えます。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .orderBy("createdAt", "desc") .limit(2) const querySnap = await queryRef.get() console.log(querySnap.docs.map((doc) => doc.data()))
output
[ { public: true, title: "another user's 1", createdAt: Timestamp { _seconds: 1710416766, _nanoseconds: 516000000 }, body: "another user's 1", updatedAt: Timestamp { _seconds: 1710416784, _nanoseconds: 973000000 }, userUid: 'ADedsD44tdFGrfgerGFErgergRRR' }, { public: true, title: '3', body: '3', userUid: 'EVyT9uSk92ddwPeBMjElL5Qx6PCK', createdAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 }, updatedAt: Timestamp { _seconds: 1710412878, _nanoseconds: 245000000 } } ]
集計クエリでデータを要約する
count()
, sum()
, average()
も使えます。
ここではcount()
の紹介だけにしておきます。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const queryRef = adminDb .collection("articles") .where("public", "==", true) const snap = await queryRef.count().get() console.log(snap.data().count)
output
3
データの追加、更新、削除
ドキュメントを設定する
set()
を使うと追加または上書きできます。
タイムスタンプに現時刻を設定するにはadmin.firestore.FieldValue.serverTimestamp()
を使用します。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const data = { public: true, title: "Add", body: "Add", userUid: "ADDUSERUID", createdAt: admin.firestore.FieldValue.serverTimestamp(), updatedAt: admin.firestore.FieldValue.serverTimestamp(), } adminDb.collection("articles").doc("ADDUID").set(data)
ドキュメントを追加する
uid
を指定したい場合はset()
を使いますが、自動的に生成したい場合はadd()
を使います。
import * as admin from "firebase-admin" const adminDb = admin.firestore() const data = { public: true, title: "Add", body: "Add", userUid: "ADDUSERUID", createdAt: admin.firestore.FieldValue.serverTimestamp(), updatedAt: admin.firestore.FieldValue.serverTimestamp(), } adminDb.collection("articles").add(data)
ドキュメントを更新する
一部のフィールドを更新するにはupdate()
メソッドを使います。set()
メソッドを使ってmerge
オプションを使うこともできますが、update()
メソッドを使う方がいいと思います。理由はFirebase Admin SDKはセキュリティルールを無視するので、マージオプションを付け忘れると一部のフィールドを消してしまうかもしれないからです(クライエント側のSDKだとセキュリティルールのおかげでエラーになることも多い)。
import * as admin from "firebase-admin" const adminDb = admin.firestore() adminDb .collection("articles") .doc("ADDUID") .update({ title: "Update" })