0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

本番の Firestore が怖すぎるので、全書き込みに「バックアップと監査ログ」を強制するアプリを作った

0
Posted at

本番の Firestore、怖くないですか

Firestore を業務で使っていると、こういう瞬間がたまにあります。

  • 本番のドキュメントを Firebase コンソールから直接ちょっとだけ直すとき
  • 「このコレクション、もう使ってないから消していいよ」と言われて削除ボタンにカーソルを乗せるとき
  • CSV でもらったマスタデータをスクリプトで流し込むとき

Firebase コンソールは優秀ですが、本番もテストも同じ見た目で、削除に取り消しはなく、誰が何をしたかのログも残りません。あの「エンターを押す直前の緊張感」を何年も味わってきました。

RDB の世界には便利な GUI クライアントがたくさんあるのに、Firestore にはちょうどいいものがない。あっても英語圏製で、日本語のデータが崩れたり、本番を守る仕組みがなかったり。

ないなら作るか、ということで作りました。

作ったもの: Firescope

Firescope は Firestore 専用の Mac 用デスクトップクライアントです。

型が見える表でデータを閲覧・編集して、CSV で出し入れして、消してしまっても戻せる。ふつうの DB クライアントがやってきたことを、Firestore でちゃんとやる、というコンセプトです。

Firescope のスクリーンショット

デモ動画(すべて実機の録画)はサイトに置いてあります。この記事では「どう作ったか」の話をします。

中核: 破壊的操作パイプライン

いちばんこだわったのが、すべての書き込みが必ず通る安全パイプラインです。

確認ダイアログ → 自動バックアップ → 実行 → 操作ログ

ポイントは、これを UI の注意書きではなく Electron のメインプロセス側で強制していることです。レンダラー(React)がどんなリクエストを投げようと、メインプロセスのパイプラインを通らない書き込みは存在しません。UI のバグや将来の実装ミスで安全装置が外れることがない構造にしています。

環境ラベル × 危険度のマトリクス

接続ごとに「開発 / テスト / ステージング / 本番」のラベルを付けます。操作側には「安全 / 注意 / 危険」の危険度を定義して、ラベル × 危険度のマトリクスで確認の強さが決まります。

開発 ステージング 本番
フィールド編集 そのまま実行 確認 確認
ドキュメント削除 確認 確認 タイプ確認
コレクション削除 確認 タイプ確認 タイプ確認

「タイプ確認」は、プロジェクト ID を手で入力しないと実行ボタンが押せないやつです(GitHub のリポジトリ削除と同じ方式)。うっかりでは絶対に突破できません。

さらに確認ダイアログにはワンタイムのノンスを持たせていて、確認画面を出したリクエストと実行リクエストが 1:1 で対応することをメインプロセスが検証します。「昔出した確認ダイアログの OK を使い回す」ような抜け道もありません。

自動バックアップと ⌘Z

破壊的操作の直前には、影響を受けるドキュメントを自動でスナップショットします。おかげで、

  • 削除・一括更新のあとでも差分プレビューを見ながらワンクリック復元
  • 直近の書き込みは ⌘Z で即取り消し

ができます。「消しても戻せる」が保証されると、本番をさわる心理的負荷が本当に下がります。これは作った本人がいちばん実感しています。

地味に効く工夫たち

型が見える表

Firestore はスキーマレスなので、「このフィールド、string なのか number なのか Timestamp なのか」問題が常にあります。Firescope のグリッドは全列に型バッジ(string / int / time / array …)を出します。int64 は BigInt で無損失に扱い、編集時も型を保ったまま書き込みます。

CSV に型注釈を埋め込む

CSV エクスポート時にヘッダーへ型注釈を付けています。これで Excel で編集 → 再インポートしても型が崩れません。インポートは「列の型とモード(upsert / 新規のみ / 更新のみ)の確認 → 件数プレビュー → 実行」のウィザードで、上書き分は例によって自動バックアップされます。Shift_JIS も自動判定します(日本の CSV、だいたい Shift_JIS なので…)。

JS コンソール

db.collection('orders').where(...) という firebase-admin 風の API でクエリを書いて ⌘Enter、結果は型注釈つきの表で出ます。書き込みを含むコードはドライラン → 書き込みプレビュー → 適用の三段階で、コンソールからでも安全装置は外れません。

技術スタック

  • Electron + React + TypeScript + Tailwind
  • IPC は Zod でスキーマ定義した契約を main / renderer で共有。チャンネルごとに入出力の型が強制されるので、プロセス間の境界でランタイム検証と型安全が両立します
  • Firestore アクセスは firebase-admin を接続ごとの named app で管理
  • サービスアカウントの鍵は macOS キーチェーン由来の鍵で暗号化してローカルにのみ保存。外部サーバーには何も送りません

ハマりどころ: firebase-admin v14 とエミュレーター

ひとつ知見を置いておきます。Firestore エミュレーターに firebase-admin で繋ぐとき、昔は credential にダミーオブジェクトを渡す小技が使えましたが、v14 からは cert / ADC 以外の credential が拒否されます。

Failed to initialize Google Cloud Firestore client with the available credentials.
Must initialize the SDK with a certificate credential...

対処は「その場で生成した使い捨て RSA 鍵で cert() を作る」です(エミュレーターはトークンを検証しないので中身は何でもいい)。

import { generateKeyPairSync } from "node:crypto";
import { cert } from "firebase-admin/app";

const { privateKey } = generateKeyPairSync("rsa", {
  modulusLength: 2048,
  privateKeyEncoding: { type: "pkcs8", format: "pem" },
  publicKeyEncoding: { type: "spki", format: "pem" },
});

initializeApp({
  projectId,
  credential: cert({
    projectId,
    clientEmail: `emulator@${projectId}.iam.gserviceaccount.com`,
    privateKey,
  }),
});
db.settings({ host: "localhost:8080", ssl: false });

同じエラーで詰まった方はどうぞ。

LP のデモ動画は Playwright で自動録画

余談ですが、ランディングページに載せている 9 本のデモ動画は、Playwright でアプリを自動操作しながら CDP のスクリーンキャストで録画 → ffmpeg で mp4 化するスクリプトで生成しています。Firestore エミュレーターにデモデータを流し込み、疑似カーソルを合成して、人間っぽい速度で操作させています。機能が変わってもコマンド一発で全動画を録り直せるので、スクリーンショットの陳腐化問題から解放されました。これはそのうち別記事に書きます。

料金

14日間はフル機能のトライアルです(登録不要・期限後も閲覧は無料のまま)。気に入ったら Pro が月 ¥1,480、チーム(5人)が月 ¥5,920 です。

おわりに

「本番が怖い」は、注意力でがんばるものではなく、仕組みで解決するものだと思っています。同じ怖さを知っている Firestore ユーザーに届いたらうれしいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?