個人開発アドベントカレンダー2022めちゃくちゃ遅刻してしまいました、申し訳有りません…!!!
作ったもの
アンケートフォームを作れるサービスです。
OGPの動的生成や投票時のリアルタイムリアクションをするような機能を持たせています。
項目は100個まで作れて画像が使えたりコメントが可能だったり、あとから誰でもが自由に選択肢を追加できたり複数選択などもできます。
ログインなしでも使えるので良ければ使っていただけると嬉しいです。
こういうのが作れます
作成した経緯
NT金沢というイベントで友人である(@brakon)のブースが半分使えるのでなにか出展しないかという話をいただき、
彼女がたくさんプロダクトを展示する予定なので、展示を見に来てくれた人に展示物のどれが良かったかや感想を投稿できるものを作ろう。
どうせならリアルタイムで見えるほうがわかり易かろう、という流れでアンケートサービスを作りました。
(が、かなりハードウェアの祭典という感じなのでウェブサービスの展示はちょっと地味過ぎたぜ〜!とても楽しいイベントでした)
構成
Nuxt2と合わせ、すべてをFirebaseで作る、という制限を設ける挑戦をしてみました。
勉強にはなったのですが、色々とつまづいたことが多かったのでその共有としてこのアドベントカレンダーをしたためたいと思います。
Firebaseでうまく行かなかった試みについて
どんなことに躓いたのかまとめていきます
CloudFunctions(for firebase)でNuxtのSSRが出来…たが…
CloudFunctionsでNuxt2のSSRが出来るということを知ったのがこの構成と挑戦をした理由でした。
参考: https://medium.com/@sirofjelly/deploying-a-nuxt-ssr-server-side-rendering-app-to-google-firebase-5d90117167db
ただ、出来る…出来はするんですが…、もう大体の人は気づいてると思うんですがCloudFuntionsってのはコールドスタートなんですよね。
常にインスタンスが維持され続けるだけの賑わいのあるサービスなら問題にならないのかもしれませんが、アクセスする都度画面が真っ白の状態が5秒弱ぐらい続いてしまい、非常に良くないなという体験になってしまいました。
こちらの記事(https://qiita.com/takehilo/items/9ab4a25a02b8328a6d5e )を参考にさせていただきメモリ割り当て量を増やすなどの対策をしてみたのですが、遅いな〜というレベルを脱するに至らず…。
CloudFunctionsでSSRが出来るからといってもそれが良い形で動くわけではないということを知りました(当たり前ですね)。
CloudFirestoreのドキュメント、上限サイズ1MBなんですか?
まあそりゃ制限はあるだろ、という当たり前の話になぜやる前に気づかないのか…愚かですね…。
アンケートでは100個の選択肢を作れるようにしているんですが、例えばそのデータ構造として選択肢の一つ一つをドキュメントにする…とかするとドキュメント読み取り数が指数関数的に増加してしまうので、いかに知名度のないサービスであろうともそんな構造にしたらあっという間に利用料金が破滅してしまいます。
ので、どうしてるかっていうと配列フィールドを作成して選択肢となるオブジェクトを入れてます。
が、項目タイトルの通り上限サイズ1MBなので文字数がたくさん入れられるようなプロパティを生やしてしまうと、割と簡単に上限サイズに到達してしまいます。もちろんそれはすべての項目にフルマックスで入力した場合なので、運用上発生するかというとそんなことはおそらくないのですが、拡張性については諦めないといけなくなってくる部分となりました。
どうしてもやりたいことが増えたら選択肢の最大数を絞るなどの工夫が必要になりそうです。
結局投票項目へのコメント機能などは1コメント1ドキュメントの形にしてアンケートのコレクションとして生やしてしまいましたが、RDBっぽい真似をするのはあんまり向いてないなと感じました。
Firestoreの検索、貧弱すぎへん?
もうこれはマジで貧弱すぎでフォローの余地が全然ないというか、公式もAlgoliaの使用を前提にしてる感じです。
っていうかFirebaseファミリーオンリーでやるつもりが結局Algoliaを契約してしまいました。クソッ!!!
Firestoreをデータベースとして何かをやるなら、めちゃくちゃ単純なqueryだけで解決できるように匠の設計をする必要がありそうです。
逆にうまくいったことについて
良くなかったことばかり書くのはよろしくないので、良かったことについても書いておこうと思います。
CloudFirestoreトリガー、めっちゃ便利やん
https://firebase.google.com/docs/functions/firestore-events
CloudFunctionsとCloudFirestoreはトリガーイベントで連携できます。
これが非常に使い勝手がよく、ユーザーを削除したときに紐付いていたデータをぜんぶ削除したり、
アンケートの更新時にAlgoliaの情報と同期したりさせるのが非常に楽でした。
実装している内容はこんな感じです。
const admin = require("firebase-admin");
admin.initializeApp();
const fireStore = admin.firestore();
const auth = admin.auth();
/**
* ユーザー退会
*/
exports.leaveUser = functions
.region("asia-northeast1")
.firestore.document("deleted_users/{docId}")
.onCreate(async (snap, context) => {
// デバッグモードなら処理しない
if (process.env.FIREBASE_DEBUG_MODE) return;
try {
const deleteDocument = snap.data();
const uid = deleteDocument.uid;
await fireStore
.collection(`user/${uid}/surveys`)
.get()
.then((querySnapshot) => {
querySnapshot.forEach(async (doc) => {
const userSurveyData = doc.data();
await userSurveyData.ref.set(
{
isDeleted: true,
},
{ merge: true }
);
});
});
await fireStore.collection(`user`).doc(uid).delete();
await auth.deleteUser(uid);
} catch (error) {
console.error(error);
}
});
こちらの記事を実装時に参考にさせていただきました。
https://qiita.com/takashikatt/items/e17401ff301b71e821cd
新しいフィールドを増やすのが楽
KVSの本領のところですね。新しいフラグやフィールドが欲しいな、と思ったときロジックの実装だけすればOKというのはかなり扱いが軽くて楽だなあと思いました。
とはいえ、後方互換を無限に考え続けることになるのでやりすぎは禁物ですし、長く運用・拡張していくようなシステムならかなり慎重に考えてやっていくべきアプローチなんだと思います(KVSのシステムの保守をやったことがないのでぼんやり表現)。
個人開発なら失敗も成功も気楽だよね
アドベントカレンダーの主題であるところの「個人開発」というテーマについてですが、このような失敗や成功体験を気楽に誰に迷惑かけるでもなく挑戦したり学んだり出来るのが個人開発のいいところだよね、と思っています。
自分はあまり個人開発で収益をあげようとかそういうところに関心がなく何もノウハウがないので、そのあたりの話は需要がありそうなのですが何も話すことができなくてすみません。いくつかのサービスを運営していますが、トータルだと相当の赤字ですね(これは単にインフラ選定が下手なのも要因として大きいです)。
次はNuxt3、Vercel、Supabase…もしくはさくらのVPSとConoHaのレンタルDBあたりでなにかやってみようかなと思います。
国内のPaaSの圧倒的安心感もいいですよ。あとわりと安くて定額だったりしますし、さくらのVPSは愛用させていただいてますが一時的なスケールアップができないのが歯がゆく感じてたんですが、先日リソースブーストという機能が発表されもうこれでいいじゃん、最強じゃん。って顔になっています。
https://rs.sakura.ad.jp/resourcesboost.html
技術的な失敗はいくらしてもいいですが、インフラ構築ミスって青天井の請求が来る、みたいな金銭的な大失敗は個人であるがゆえに避けられるようにしたいですね。