
先日Firebaseを使用したWEBサービスをリリースしました。エンジニアチームの規模が小さく、インフラ専任のメンバーを置ける状況ではないので、できるかぎりフルマネージドなプラットフォームを選択しました。
Firebaseを利用するにあたり調査した情報をまとめます。
目次
Firebase + Reactについて
Firebaseを使い、ReactのクライアントからDB(Firestore)を操作することにセキュリティ上の不安がありました。サーバレスな環境構築をするにあたり、セキュリティはjwtを使用することで担保しており、サーバーサイドのコードを書かなくて済みます。
開発コストも削減することもでき、スケールに関しても問題ないので、1〜数人の小規模チームで開発する時は選択肢の1つとしてオススメできるものでした。
Reactのcreate-react-appコマンド等を用いた詳しい開発手法は、他のサイトにも多々記載があるので本記事では割愛させていただきます。
調査をしていく過程で、Firebaseを使用したプロジェクトの実績、クックパッドのチームのインタビュー記事を見つけました。
SSRについて

サーバレスなアプリを構築するにあたり、SSRをするかの問題です。ReactのNext.jsを使用してSSRの対応も考えました。SSRのメリットとしては、初期表示とSEOの観点が多いかと思いますが、結果として以下の理由でSSRしないSPAとして構築を進めました。
- 主にPCで使用するWEBのツールサービスのアプリのため、SSRによる初期表示とSEOに重きを置いていない。
- サーバレスな環境(cloud function)でNext.jsでSSRをするために、リクエストを全てFirebaseFunctionに流すことができますが、レスポンスが遅い?
- Next.jsでSSRをするためには、app/package.jsonとfunctions/package.json(Cloud Function)の設定をいちいち同じにしなくてはならない。
- サーバレスな環境・SSRにしてしまうと 実装に縛りができて難しくなるので、開発速度は確実に低下。以後全てのReactコンポーネントが、初期stateからのpropsバケツリレーできっちり表示する必要がある。
- SSRしなくてもSEO・OGP対策は可能
SEOとOGPをどうするのか
以下は実際にプロダクション環境でサーバレスアーキテクチャを使用し、SEOとOGPに対応している情報です。
SPA用のアクセスポイント(index.html)を置く、metaだけ入ったhtmlを返す。
Cloud Functionsでindexのmetaタグを差替Cloud FunctionsでRSSフィード配信
スプレッドシートでsitemap公開
AMP化したHTMLファイルをCloud Storageを使ってサーブする方法です(Cloud FunctiosのレスポンスはCDNにキャッシュできるので)
Firestore(NoSQL)のDB設計とモデリングツール
FirebaseにはFirestore(NoSQL)がDBとして用意されています。realtime databaseよりも値段も安く、クエリの検索も充実しています。
また近年NoSQLデータベースへの注目が急激に高まっている1つの理由として、データやアプリケーションの要件変更に柔軟に対応できるということがあります。NoSQLとアジャイル手法は相性が良いです。というのも、これらはどちらも変化・変更を嫌がらずに受け入れることができるからです。
NoSQLのDB設計Firestore の場合、トランザクション要件が厳しいので、正規化崩しまくってフラットスキーマにするのが良さそうだと判断しました。1対n関係のエンティティもできるかぎりArray型にして1エンティティの中に入れる。といった感じで設計するのが正しいのではないかと思います。RDBの考え方は捨ててください。
- 冗長型(1対1)
- ネスト参照型(1対 N)
- リレーションシップ参照型(N 対 N)
フィールド名はスネークケースにしました
apiで返却されるjson objectのkeyにはよく使われますし、DB値の制約により利用されることがあるからです。正解かはわかりません。。
NoSQLデータベースの欠点を補う機能としてSubCollectionがありますが、NoSQLのデータベース設計を理解しないと、初めて触る方は難しく感じるかもしれません。
その他に注意する点として、Firestoreにはauto-generated keysやFirebase Realtime Databaseのpush IDsがありません。チュートリアルにもありますが、ID を自動的に生成するような設定を行うには、add() を呼び出します。Firebase Realtime Database の push ID とは異なり、Firestore で自動生成された ID の並べ替えは自動的に行われません。ドキュメントを作成日で並べ替えるには、ドキュメントのフィールドとしてタイムスタンプを保存する必要があります。開発においてもソートで利用することが結構あるので、createdAtは保持しておくのがおすすめです。
セキュリティルール設定
Firestoreへの接続情報はクライアントや通信内容などから容易に取得可能なため、このままでは全データがだだ漏れですし簡単に破壊されてしまいます。
よくあるWeb APIサーバー + データベースサーバー構成ではデータベースサーバーへの接続許可をWeb APIサーバーなど一部に制限することでセキュリティを担保しますが、Firestoreはクライアントから直接接続する方式なのでそういうわけにはいきません。
Firestoreではセキュリティルールにより読み取り・書き込み権限を過不足なく設定することでデータを守ります。
メールの送信
メール送信周りはSendGridをcloud functionを通して実現しました。Google公式のドキュメントページがあるので、そちらを参考にしていただけると良いと思います。
決済はStripe
決済周りはStripe公式の開発ドキュメントとライブラリが充実しており、React用のコンポーネントなどもしっかり用意されています。stripeは大きく分けて2通りの使用方法があります。customerを作成しての決済(会員情報)としない決済(一度きりの決済)。
reactのコンポーネントにはcustomerを作成しない決済の対応しかしていなかったので、アカウント情報にカード情報を紐づけて決済をする際には以下のような流れにしました。
- customerを作成(stripe.customers.create)
- cardを登録しtokenを発行(stripe.tokens.create)
- customerに発行したtokenを登録(stripe.customers.createSource)
- customer idとcard idを使用し決済処理を行う
まだ機能は少ないですが、Googleがstripeを利用した「Firestripe」という決済機能を構築しています。決済機能まで独自のインフラを構築せず簡単に導入できるようになれば、世の中のFirebaseの導入が進むことになるでしょう。
Firebaseでcronする方法
Cloud Functions for Firebase でジョブをスケジューリング(cron)します。githubに記載されている方法で簡単に構築することができました。