見た映画の感想を管理するアプリ「FilmDeX」を作った 後編
自分が見た映画のデータベースを作ろうと思い、試行錯誤していた話の続き。
今回の記事で書いた試行錯誤を経て作られたソースコードは以下。
実際のウェブアプリは以下。
目次
課題
目下の課題は2つ。
- Node.js 製のアプリケーションサーバをどこにデプロイするか (課金はしたくない)
- どうやって無料でデータ永続化層を用意するか
である。
アプリケーションサーバのデプロイ先
PHP や Python なんかだと、割と対応している無料のレンサバや PaaS が多いのだが、Node.js は微妙に少ない。
FlashDrive.io というサービスが Heroku 代替になりそうだったので登録してみたのだが、登録翌日に +1
番の電話認証を求められるようになってしまい、サービスを使えなくなってしまった。+1
というのはアメリカの電話番号のこと。日本の電話番号しか持っていない人間は FlashDrive.io を使い続けられなかった。Node.js も動くし各種 DB も使えて良さげだったのに…。
- Heroku 代替サービスを探していた形跡
無料サービスのドッグフーディングは趣味として並行するとして、今回の件はどうしようか。NestJS と TypeORM を使ったサーバサイドの実装は結構掴んできたし、無理にサーバサイドの資産を残さなくても良い気がしてきた…。DB 次第で FaaS に切り替えるか…?
無料のデータ永続化層を探す
NoSQL 系だといくつか無料サービスがあるのだが、まだ自分が「結果整合性」の仕組みに慣れていないこと、TypeORM + SQLite で作った資産がほぼ作り直しになることから、二の足を踏んでいた。
というか、無料サービス系のほとんどにいえるのだが、データのバックアップ、エクスポートの仕組みがない・弱いところが多い。「自分が見た映画リスト」は大事なデータなので、バックアップをちゃんととりたいのだが、無料枠でなんとかしようとすると、その辺が犠牲になりがち。どこぞの知らないウェブサービスに、自分の大事なデータをバックアップなしでは置きづらい。
信頼性も踏まえると、結局 Heroku に戻るのはどうだろうと考えていたのだが、親子テーブルを持つ関係上、1万行の上限に引っかかりそうでちょっと断念。今動かしてる Neo's Hatebu の引っ越しも大変だしね~。
…
ところで、最初に投入するつもりのデータだが、今まで見た全ての映画を記録しているワケではなかったので、まずは自分のブログの過去記事から拾えるだけ拾ったデータだったり、Wikipedia からかき集めたデータだったりをスプレッドシートにまとめていた。
そしてふと、この映画一覧のスプレッドシートを見返していて気付いた。コレが JSON 形式で取得できればいいのか。
つまり、データ永続化層を Google スプレッドシートにする。コレなら、編集権限はアカウントを持つ僕一人だし、何らかの方法で参照のみパブリックアクセス可能になれば問題ないな、と考えた次第である。
Google スプレッドシートのデータを JSON 形式で取得する
Google スプレッドシートは詳しくなかったが、多分何かしら JSON 形式で取得する API があるだろうと踏んでググってみた。あった。
GCP で API キーを取得し、それを用いてシートデータを取得する方法が使える。昔は「ドキュメントを公開」することで JSON 形式で取得できたようだが、現在は使えなくなっている。
JSON 形式でシートデータを取得する方法はあったものの、GCP の API キーを URL パラメータに含める必要があるので、SPA から直接 API コールするのはダメだと判断した。API キー文字列を秘匿しきれないからだ。
それと、ちゃんと調べていないが、恐らくは API のコールレート制限もあるんじゃないかしら。そういうことも加味すると、やはり SPA から Google Sheets API をコールしてデータを取得するのは無理そうだなーと判断。
そこで、GitHub Actions で週に1回、スプレッドシートのデータを JSON ファイルに書き出し、そのデータを含めてフロントエンドプロジェクトをビルドするようにスケジュール設定してみた。API キーおよびスプレッドシートの URL は環境変数で秘匿している。スプレッドシートの変更をリアルタイムには反映しなくなるが、あんま困らないべ。一応 schedule.cron
設定とともに、手動実行もできる workflow_dispatch
も設定しておいたから、スプレッドシートを大量更新した時は自分で GitHub Actions を再実行してやることにする。
GitHub Actions 内で、スプレッドシートを取得し JSON ファイルに書き出す処理を実行しているが、この Node.js スクリプトはローカルで単体でも実行できる。気分次第でローカルでこのスクリプトを実行し、JSON ファイルを取得したら Git コミットする。コレで、万が一スプレッドシートを失っても、その中に書かれていたデータのバックアップが JSON 形式で残ることになる。Git リポジトリをバックアップに利用するワケだ。Angular プロジェクト的にはスプレッドシートが存在しなくても良くて、assets/
配下に出力された JSON ファイルさえ存在すれば画面表示できるので、スプレッドシートを置いている Gmail アカウントがどうにかなっても、最悪この Git リポジトリさえ残れば、データ損失は少なく済む。
SQLite 管理から Google スプレッドシートに移行したことでのデメリットというと、「キャスト」「スタッフ」「タグ」情報の列もただの文字列として管理することになったところ。SQLite ではそれぞれを子テーブルで持っていて、1つの映画に対し n 個のキャスト情報がぶら下がるような構成にしていたのだが、スプレッドシートでは普通に1セルに複数の人物名やタグを書き連ねている。フォーマットもシステム的な制限はかけていないので、自分で考えている書式を自分で徹底しながら書くしかない。
コレに伴い、フロントエンドの検索処理もちょっとおざなりになっていて、「キャスト」「スタッフ」「タグ」それぞれの「セル」内の文字列全てを直接検索している。なので、例えばキャスト欄に「アーノルド・シュワルツネッガー、リンダ・ハミルトン」と書かれた映画情報があったとして、キャスト名検索欄に「ガー、リン」と入力しても検索にヒットする、雑な作りとなっている。まー皆そんなに気にしないだろうということで、このままにする。w
以上
当初は
- Angular + NestJS + TypeORM + SQLite
というサーバサイド込みの構成だったのが、
- Angular + GitHub Actions + Google Sheets API
というフロントエンドオンリーの、コンテンツデータを JSON で持った SSG に近い構成になった。
NestJS は Angular と似た感覚で開発がしやすかったし、JWT トークン認証なんかの機能も簡単に作れた。TypeORM や Mongoose 等の O/R マッパーと接続するモジュールもあるし、とても便利で機能的には問題なかった。
ただ、Node.js アプリケーションサーバを無料でデプロイできる場所探しに苦戦し、またバックアップも考慮して無料のデータ永続化層を探し求めた結果、今回は NestJS で実装した部分をまるっと捨てることになった。
それと同時に、スプレッドシートを DB 代わりにするのが結構良いというか、やっぱり Excel で足りるわーみたいなところに至った。
作り終わってから思ったのだが、Google Sheets API をコールする API キーの秘匿のためには、Google Apps Script でプロキシ的な API を作ってやる方法もあったなーと思った。GitHub Actions でやっているシート取得処理を GAS で実装してやれば良かったのかもしれない。というか、GAS なら API キーを用意することなくスプレッドシートにアクセスする機能がデフォルトでありそう。
…というか、もう Google スプレッドシートを表示専用で共有 (一般公開) しちゃえば良いんじゃないかという気にすらなってきた。もはやフロントエンドすら要らない疑惑。やっぱり Excel で足りるわーって感じ?
まぁいいや、元々は NestJS が使ってみたくて始めた企画だし、見せ方なんかどうでもいいってことに気付いてしまったところで、このアプリの開発はおしまいかな。アプリはともかく、映画のレビュー情報は今後もどんどん加筆していくつもりなので、よろしければ映画情報のいちコンテンツとしてご覧ください。