先日、Togetter社長の@yositosiさんとひょんなことからお友達になり「なんかFirebase使って面白いことやろうよ」という話になったので一緒に面白いことをやりました。
この記事は、
- Firebaseを使うと何ができるのか
- Nuxt.js/Vue.jsとFirebaseの勘所
- Firestoreでの複雑なクエリ処理にどう対応するか
などのトピックを中心に紹介していければと思っています。
なんとかPay
Togetterの社長の@yositosiさんからFirebaseを使ったアプリ構築の話を頂きお手伝いさせていただいた、エイプリルフールの企画の「なんとかPay」というサービス。誰でも自由にPayを発行できる、昨今のPay蔓延している社会に物申しているようなそうでもないようなそんなサービス。
今回は技術周りで色々とお手伝いをさせていただきました!
自分だけのPayが作れる
リアルタイムで状況が更新される
こんな感じでOGP画像も生成される
自己紹介
モノづくりをしたりプログラミング先生をやったり。最近はReactNative触ったり色々。
- Railsで歌声専門フリマサイト
- React勉強で今期アニメ検索アプリ(Qiita記事)
- Nuxt×Firebaseで文字を画像に変換するアプリ(Moji → Pic)(Qiita記事はこちら)
とか作ったりしてます。また、Web系言語やらのネタでブログにTIPSを書いたり、Twitter(@y_kawase)でもちょこちょこ真面目ネタも呟いているので宜しければ絡んで下さい。お仕事依頼もお気軽に絡んで下さい。
ゆるいネタでnoteも始めてみたので、もし興味があればこちらもご覧いただけると嬉しいです。
構成など
Nuxt on Firebaseな感じでやっています。細かい構成は大体以前書いていた記事の構成とも被るのでこちらもぜひ → Webアプリ無料運営のススメ:FirebaseとNuxt(Vue)なら最強!
若干はしょってますが、こんな感じの構成です。Firebaseで完結とか言いながら、定期タスクの実行にGoogle Cloud Schedulerこっそりつかってます。これはいわゆるcron的なやつ。HTTP叩くとかPub/Sub叩くとかできるので、GCP系で開発している方はぜひ使うとよい。(最短1分間隔jobを作れて、3jobまで無料)
ーー追記ーー
FunctionでのSSRは少し古い内容になります。先日(2019/4)に発表されたCloudRunを使ってSSRをするのが今後は主流になるかと思われます。
https://firebase.google.com/docs/hosting/cloud-run
使ったライブラリでメインのところはこのあたり。
- nuxt
- vuexfire / vuexとfirebaseをうまく融合
- twemoji / 絵文字
- node-qrcode / QRコード生成
- 検索で上に出てくる太古のqrcodeライブラリ使わないように注意。日本語とかに対応してないから。新しいのをつかうよう。
- https://github.com/soldair/node-qrcode
- dayjs / momentより軽い時間周りの処理
- base64url / Payの名前をIDに変換したい時に使った
- 後述してますが、日本語ID名だとバグる時に、一意な変換をしたかったのでbase64url使いました。
- puppeteer / Function側でOGP画像を作る時に使った。
- puppeteer内部でcanvas描画したやつをスクショ的な感じ。ただ、実装が大変だし、Functionも重いので、クライアント側で作ってstorageにアップが楽
- ただ、端末依存しない画像が作れるメリットは大きい(端末依存云々の話は、以前私が作った文字画像変換アプリでも話題になっていた話です)
FirebaseとNuxtは便利
Firebaseはいいぞおじさんとして最近この話ばっかりしてる気がしますが、サクッとアプリが作れるので、色々作りたいひとにはおすすめしたい。もちろん初心者はRails×Vueとかでもいいとは思うので、そのあたり飽きてきた中級者あたりは是非。
昨今流行りのJAM Stackな開発云々の知見含め、サーバーレス、SPA、SSR、PWAあたりの知識も一通り手に入るので、腕に覚えのある方は是非触ってみることをおすすめしたい。
Firebaseを使って良かったところ
認証が大変楽に
このご時世、認証周りを自分で書いてる人なんているんです?(煽り)というくらい、認証はとは面倒なもの。セキュリティ的にも、自分で頑張って作るより、アリものを使わせてもらうほうが絶対良いです。もちろん勉強したいひとは一から作ったほうがためにはなる。本アプリは、Twitter認証だけでしたが、FacebookやGoogle認証も可能です。最近はAuth0もアツいですが今回は割愛。
Firestoreはシンプルながら高機能
Firebase(NoSQLなデータベース)は簡単にリアルタイムデータ同期ができます。今回はこれが一番このアプリで効いていたなと思いました。トップページのアクティビティで、誰かがPayを作ったり送金する度に、リアルタイムで状況が更新されるので、見てるだけでも楽しいものになりました。
こんな感じ。
使ったライブラリ:vuexfire
もちろん他のDBでも似たようなリアルタイム反映は頑張ればできるんですが、サクッと実装できるところがすごい。
Nuxt使ってて良かったところ
SSR設定が楽
耳にタコなネタですが、サーバーサイドレンダリングが簡単にできます。いわゆるツイートとかした時に、OGP画像がちゃんとページ毎に各々設定されるのとかは、このSSRのお陰でもあります。
PWA設定が楽
PWA対応してて力入れてるようにみえて、実はNuxt様のプラグイン入れてちょこっと設定ファイル書いただけ。NuxtはPWAやらSSRやらVuexやら初期状態でも全部入りな感じで好き。
Firebaseの取扱で大変だったところ
Firestoreでユニーク扱いをしたい時
Payの名前で同名を避けたかったのですが、このユニーク設定の実装が意外と面倒でした。RDBMS系だと普通にできるuniq指定などは存在しないので、自分で実装する必要がある。具体的には、
- FirestoreのドキュメントのID名をユニークにしたい名前にする
- トランザクション機能を使って、既存テーブルを検索して存在しなければ追加
のような実装が必要になる。また、ユニークにしたいものを直接ID名にするため、日本語を取り扱うときは、base64urlなどで適宜ID名をエンコードしておかないと、文字周りで面倒もあったりする。このへんリレーショナルDB系(MySQLとか)でさわってた人はすごくイライラするとは思う。
Firestoreでの権限操作
Firestoreはクライアント側から直接触りに行くので、、ユーザによる変更権限などの制御が意外と小回りが効きにくくてキツいイメージがあります。
ーー追記:セキュリティルールでいけた内容だったかも(詳しくはコメントに記載)ーー
https://qiita.com/y_kawase/items/1de690b40553ef76acb3#comment-02654fd64fa4c26faca9
たとえば一例として、なんとかPayでは、それぞれの個々人がPayのお財布を所持しています。
例えばこんな感じで権限ルールを書いて、money(財布)テーブルの中に持ち主のuid(ユーザID)とかを埋め込んでおけば、
- Aさんの財布は全員見れる(read)
- Aさん自身は財布の中身を出し入れできる(create, update)
service cloud.firestore {
match /databases/{database}/documents {
match /money/{moneyId} {
allow read;
allow create, update: if request.auth.uid == resource.data.uid;
}
}
}
}
みたいな感じで、一見すると要件を満たしそうにみえるのですが、
- BさんがAさんの財布に1000円送金する(Aさんデータをupdate)
という操作は権限的にアウトになります。なら、他の人もupdateできるようにすればいいだけじゃんというのが自然な考えなのですが、そうすると、今度は、
- BさんがAさんの財布の中身を勝手に抜き取る(Aさんデータをupdate)
みたいなことも可能になってしまうのです。
普通のRailsみたいなサービスだと、そんな処理はControllerに書かねーぞってなるのですが、クライアント側で動いているJavascriptでの挙動&APIを投げてFirestoreデータをいじるため、やっちゃうとこのあたりの改竄リスクが高まります。
Firestoreでの権限操作問題の私なりの結論
先の権限問題が出てくる場面は「Aさんが作ったデータに対して、Bさんが何か処置をしたい」という操作がある場合に出てくる問題です。そういう意味では、TODO管理アプリとかのような他者が介入しないようなシステムならば、上記のルールベースな制御だけでも全く問題はないので、そこだけは勘違いしないように!!!。
ということで、解決策は、
- ユーザは基本的にreadだけ許可する
- createやupdateといった書き込み系操作については、Function側に委ねる
が一番しっくりくるかと思います。つまり何かデータの更新がしたい時は、Bさんは一度Function側のAPIを叩いてもらって、Function側で内容に問題がなければ、Function側が管理者権限でデータの更新をする、といったような流れになるかと思います。
こうすると、各テーブルの書き込み系処理毎にAPIのエンドポイントが必要になってきてしまう問題もあるので、FirebaseFunctions上にGraphQLサーバーを実装するとかでエンドポイントをまとめるのもいいかなと思いました。(今回はここまではやらなかったのですが)
まとめ
技術まわり
複雑なDB操作が絡んだアプリを作る時には意外とFirestoreがネックになることがありそうでした。Firestoreはクエリなどの発行も制限がキツいので(例えばOR検索できないので各自結合する必要がある、!=のクエリが発行できないなど)、そのあたりとどう折り合いをつけるかという所になるかと思います。 → 参考:Firestore - クエリの制限事項
要件がそこまで複雑なものでなければ、実装する時に
- Firestoreに直の書き込み系を禁止
- Function上でApolloServer(GraphQL)動かして複雑なクエリや書き込みをこっちで担う(query, mutation)
- Apolloのsubscription機能はFirestoreのリアルタイムアップデートで代用
っていうやり方に予め確定しておけば、デメリットは消せるかなと思ってたりします(Apolloじゃなくて、普通にFirestoreの機能直書きでもOK)。Function × GraphQLはこのへんが参考になりそう → Firebase Functions 上に GraphQL サーバーを実装する
ーー追記:セキュリティルールでいけた内容だったかも(詳しくはコメントに記載)ーー
https://qiita.com/y_kawase/items/1de690b40553ef76acb3#comment-02654fd64fa4c26faca9
とりあえずはFirestoreは強力ながらもシンプルなデータベースではあるので、とりあえずFirestoreで作り始める前に、このアプリではどういうクエリを発行するのかを事前に考えた上で、複雑なクエリ操作が絡みそうなシステムだったら、最初からGCPの「Cloud SQL」を使うのもいいとは思う。
※もちろんCloud SQLだと、時間に対する課金になるので、サーバレスの旨味は減ってしまうとは思うけど。
サービスまわり
なんとかPayは、エイプリルフールネタとして、また、大喜利のようにして使って遊ぶアプリとして皆様に楽しんでいただいている所はありますが、「LTした人にポイント授与」「毎週頑張った人にポイント付与」みたいな感じでオリジナルな使い方を見出して、自分だけのポイントサービス的な真面目な使い方もできるとは思いますので、一度触ってみて下さい。
遊びにもガチで取り組むTogetter社は流石だなという所を今回ヒシヒシと感じました。社風にも現れているのでしょうか転職エントリみてるとめっちゃ楽しそうにしてる。 → トゥギャッター株式会社に転職しました
勉強するときのオススメ
Vueドキュメント
公式ドキュメント侮ることなかれ、読みやすいです。ちゃんと日本語ですぞ。
https://jp.vuejs.org/v2/guide/
Nuxtドキュメント
こちらの公式ドキュメントも読みやすい。日本語。
https://ja.nuxtjs.org/guide/
Firebaseドキュメント
こちらの公式ドキュメントも読みやすい。ただ章立てから欲しい情報が若干見つけにくいときもある。日本語。
https://firebase.google.com/docs/?hl=ja
書籍:Vue.js&Nuxt.js超入門
掌田先生著。Vue・Nuxtに加えて外部API連携(Firebaseも少しある)も扱っているので、ガッと体系的に勉強したい時などに大変良い。
さいごに
本記事をここまでお読みいただきありがとうございました。何か素朴な疑問でも、お仕事の依頼でも、何か気になることがあれば是非Twitter(@y_kawase)で絡んで下さい。
直接ビデオ通話で相談したいぞという方はこのあたりから。
過去記事・関連記事も宜しければどうぞ
- Reactで誰もがやりたかった10の機能。アプリ構想はあるけど作れない人の壁をぶっ壊す。
- Webアプリ無料運営のススメ:FirebaseとNuxt(Vue)なら最強!
- Qiita初投稿でも狙ってバズらせられた話『5つの成功と2つの失敗』
ゆるいネタでnoteも始めてみたので、もし興味があればこちらもご覧いただけると嬉しいです。