Node.js × Laravelで安全にデータ移行する方法
Firestore(NoSQL)から MySQL(RDB)へデータを移行するケースはそこまで多くありませんが、
**「プロトタイプはFirestore、本番や分析基盤はRDB」**という構成では避けて通れない作業です。
この記事では、実際に運用している
Firestore → JSON → MySQL
という2段階構成のデータ移行フローと、その内部ロジックを詳しく解説します。
目次
- 全体の流れ
- 実行手順とコマンド
- Step 1: データ抽出(Firestore → JSON)
- Step 2: データ確認(JSON → Scan)
- Step 3: DB初期化(Reset)
- Step 4: データ移行(JSON → MySQL)
- 処理の詳細ロジック
- 移行順序
- データ変換
- エラーハンドリング
- スキーマレス対策
全体の流れ
今回のデータ移行は、大きく 2ステップ構成 です。
① データ抽出(Node.js)
Firestore 管理者SDKを使い、
指定したコレクションのデータを JSONファイルとして書き出す。
- 実行スクリプト:
extract_data.js - 出力先:
backend/storage/app/firestore_data.json
② データ移行(Laravel)
Laravelの Artisan コマンドで JSON を読み込み、
MySQL用に変換しながらインサートします。
- 実行コマンド:
php artisan migrate:firestore
Firestoreと直接MySQLをつなげないことで、
- データを目視確認できる
- 何度でも再実行できる
- ロジックを分離できる
というメリットがあります。
実行手順とコマンド
Step 1: データの抽出(Firestore → JSON)
Firestoreからデータを取得し、ローカルJSONとして保存します。
実行ディレクトリ
functions/
実行コマンド
cd functions
unset FIRESTORE_EMULATOR_HOST && node extract_data.js
何をしているか
- Firestoreの指定コレクション(例: users, sessionsなど)を取得
- JSON形式でファイルに保存
設定箇所
抽出対象のコレクションは extract_data.js 内で定義します。
const collections = [
'users',
'tables',
'sessions',
...
]
Step 2: データの確認(JSON → Scan)
MySQLには書き込まず、
JSONの中身だけを解析・確認します。
docker exec westside_php php artisan migrate:firestore --mode=analyze
できること
- 各コレクションの件数チェック
- フィールド構造の把握
- 欠損データの事前確認
👉 いきなり migrate しないのが重要です。
Step 3: データベースの準備(DB Reset)
⚠️ 注意:すべてのデータが削除されます
docker exec westside_php php artisan migrate:fresh
テスト環境・検証環境でのみ実行してください。
Step 4: データの移行(JSON → MySQL)
いよいよ本番のデータ移行です。
docker exec westside_php php artisan migrate:firestore --mode=migrate
処理内容
- JSON読み込み
- Firestore特有の型変換
- 外部キーの解決
- MySQLへインサート
処理の詳細ロジック
ここからは MigrateFirestoreData.php の内部処理について。
1. 移行の順序(外部キー対策)
外部キー制約を守るため、移行順序は非常に重要です。
- Users(ユーザー)
- すべての基点。依存なし
- Tables(ビリヤード台)
- 依存なし
- Sessions
- User / Table に依存
- MiscPurchases
- User / Session に依存
- TableHistories
- User / Table / Session に依存
- BackDoorCodes
- Entries
- PayPayPayments
この順序を守らないと、外部キー制約で必ず失敗します。
2. データ変換処理
Firestore特有の型を、MySQL向けに変換します。
🔹 ID
- FirestoreのドキュメントID(文字列)
- → MySQLの
VARCHAR主キーとしてそのまま使用
🔹 Timestamp
Firestoreの
{
"_seconds": 1700000000,
"_nanoseconds": 0
}
を、PHPの Carbon インスタンスへ変換。
🔹 Reference
Firestoreの
users/USER_ID
のような参照は、
USER_ID
のみを抽出して外部キーにセット。
3. エラーハンドリング(不整合データ対策)
部分抽出(例: limit付き)や欠損データがあると、
参照先が存在しないケースが発生します。
そこで以下のチェックを実施。
✔ ユーザー存在チェック
- 紐づく
user_idが存在しない場合 - → そのレコードごとスキップ
✔ セッション存在チェック
session_idが存在しない場合- →
nullにする or レコードをスキップ
👉 「止めない・落とさない」設計が重要。
4. スキーマレス対策(Firestoreあるある)
Firestoreはスキーマレスなので、
- フィールドがある / ない
- 型が微妙に違う
という状況が普通に起きます。
移行スクリプトでは以下を徹底。
$data['amount'] = $doc['amount'] ?? 0;
$data['memo'] = $doc['memo'] ?? null;
isset()?? null- デフォルト値指定
で MySQLのスキーマに必ず合わせる。
まとめ
Firestore → MySQL の移行は、
- 一発でやろうとしない
- JSONを挟む
- analyze → migrate の二段構え
- 外部キー順序を絶対守る
これだけで、失敗率がかなり下がります。
Firestoreは便利ですが、
**「あとでRDBに持っていく可能性がある」**なら、
最初から移行を見据えた設計をしておくのがおすすめです。

コメント