キカガク プラットフォームブログ

株式会社キカガクのプラットフォームブログです。エンジニアやデザイナー、プロダクトマネージャーなどが記事を書いています。

SQL にどっぷり浸かってきたマンが初めて Firestore を触った時に受けた5つの衝撃

バックエンド環境を提供するサービスとして今や名高い Firebase。

使ってみたいけど・・・ データベースが NoSQL 。 触ったことないしなあ。ちょっと敷居が高いなあ・・・。

そんなあなたに。

SQL に累計5年以上浸かってきた開発チームの新顔古川が初めて Firestore を触った際に体験した5つの衝撃を紹介いたします。

対象読者

SQL を触った経験がある人で Firestore に挑戦してみたいけれど「NoSQL って難しそう・・・」と尻込みしている人。

目的

  • あるある体験談を通して使用シーンを疑似体験する
  • Firestore へのとっつきやすさアップ

今までのバックエンド経歴

PostgreSQL/MySQLPrisma や Supabase などを使いながら Rest ・ GraphQL を構築することでバックエンドとしていました。NoSQL は Azure Cosmos DB で2ヶ月ぐらい触ったことがある程度です。Firestore は開発チームにジョインしてから初めて触りました。

SQL にどっぷり浸かってきたマンが初めて Firestore を触った時に受けた5つの衝撃

1. クライアントから直接叩くことが「推奨されている」

Realtime Database の洗礼を受けました!

これまでは DB をクライアント側から直接操作することは(Supabase 使用とかだとまた話は変わってくるとは思いますが)かなりおっかないことだという認識が強かったためギャップにびっくりしました。

大きなメリットとして、「複数のプロダクトにまたがって共通のデータベースを操作したい時にわざわざバックエンドを構築しなくても各々のプロダクトから直接データベースを叩けば OK!」な点が挙げられます。プロジェクトの規模がスケールすればするほど恩恵を教授できること間違いなし。すべてのクライアントとほぼリアルタイムで同期が可能な Firestore ならではの強みなのかな・・・!と個人的には思いました。

2. Reference ・ Snapshot の違いがわからず混乱

触り始めの時 DocumentReferenceDocumentSnapshot を意識せず使った結果「あれ・・・?どっちがどっち??」となることがしばしばありました。

  • DocumentReference・・・ドキュメントの参照先。CRUD 操作する時に使う
  • DocumentSnapshot・・・データの実体。DokumentReference を指定することでデータを取ってくることができる

Reference は参照、 Snapshot は実体」と頭で念仏のように唱える必要が・・・。

3. offset がない!?

瞬間思ったこと:ページネーションどうすんねん。

次のページ・前のページは簡単にできそう

クエリカーソルを使えばいい感じに実装できそうです。

import { collection, doc, getDoc, query, orderBy, startAt } from "firebase/firestore";
const citiesRef = collection(db, "cities");

const docSnap = await getDoc(doc(citiesRef, "SF"));

// Get all cities with a population bigger than San Francisco
const biggerThanSf = query(citiesRef, orderBy("population"), startAt(docSnap));
// ...

Firebase 公式ドキュメントより

ページ数を指定するパターンが厄介?

「次のページ」「前のページ」「もっと読む」の実装は簡単にできますが、ページ数をクリックして○ページ目の何かを一覧表示するときはどうするんだろう?と調べてみるもなかなか情報が出て来ずに苦戦しました。

現実的な手段として2点ありそうです。

手段1:クエリカーソルを使う

orderBylimitstartAt を駆使する方法。具体的な実装方法は下記の公式ドキュメントが大変参考になります。

クエリカーソルを使用したデータのページ設定 | Firestore | Firebase

比較的お手軽に実装できる反面、ページ数をクリックするたびにクエリが走ることになるので全体的なクエリの呼び出し回数が爆増しそうな気も・・・?引数も数が多くなってしまうのでテストが煩雑になり実装コストが高まりそうです(本音)。

手段2:最初から中身全てをフェッチしてページを振り分け状態管理する

手段1をなんとか簡潔に実装できないか考えて導き出した結論がこちら。SQL でこれをやるのはかなり抵抗があるのですが、読み込み得意な NoSQL なら捌いてくれるやろの精神で試すことが可能に。数千件〜規模になってきたら「最初の100件ぐらいとりあえずフェッチして振り分けておく」で対応できそうです。

SWR などでページ状態を状態管理しておけば UX も◎。

4. 「設計上の美しさ」と「従課金制」の板挟みが起きがち

Firestore では「読み込み・書き込み回数」に応じて課金量が決まるため、できるだけ回数を減らす設計にしていきたいという下心が往々にして発生しがちです。その際 DB 上の設計のベストプラクティスとバッティングすることがあります。

「読み込み回数を増やさずに DB の構造をどう綺麗に保つか?」を余分に考える必要が出てくるところにじわじわ衝撃を受けました。

5. 【NoSQL】1:n リレーション表現のクセが強い

SQL に慣れた人が最も困惑する部分な気がします。触り始めてしばらくは「正規化しなくていいんだ・・・正規化しなくていいんだ」と唱えながら作業することに。スキーマレスな NoSQL らしく表現方法が無数にあるのも最初は取っ付きづらく感じました。

参考に書籍を漁ったり実際に手を動かしてみて一番わかりやすかった方法を紹介します。「サブコレクションを作成する」方法です。

例えば「特定のユーザーの買い物かごの中身を表示したい」ときは・・・

user/{userId}/shoppingCartコレクションを作成しておき、適宜呼び出しを行います。user/{userId}/shoppingCartコレクション内のドキュメントを全て取得する=買い物かごの中身一覧が取得できるイメージです。

結論:直接手を動かしてみれば案外すんなり馴染む

案ずるより書くが易し!

これに限った話ではありませんが書きながら習得するのが最短ルートだなと思いました!

開発チームでは Firestore を初めとしたモダンな技術をふんだんに使って開発を進めております。

最先端の開発環境で働いてみたい方、まずはカジュアル面談でざっくらばんにお話いたしませんか?

気になる方は採用情報もぜひ覗いてみてくださいね!