Firexportガイド
CSV と JSON を Firestore にインポートする方法
Firebase コンソールで Firestore にドキュメントを入れる手段は、1 件ずつ追加するドキュメントを追加(Add document)だけで、ファイルを読み込むボタンはありません。Google 自身のインポートである gcloud firestore import が読むのは Firestore 独自のエクスポート ファイルだけで、CSV や JSON は読めません。ファイルをコレクションに入れる方法は 3 つあります。どれが合うかは、ファイルの大きさと、ファイルがどこから来たかによります。
2026年10月21日 更新。 方法 1 の拡張機能 Firexport は私たちのものです。方法 2 と 3 には不要です。
結論から言うと
| 1. コンソール + 拡張機能 | 2. Admin SDK スクリプト | 3. gcloud のマネージド インポート | |
|---|---|---|---|
| 準備 | Chrome 拡張機能をインストールし、Google アカウントを接続 | Node.js と Google Cloud の認証情報 | Cloud Storage バケットと gcloud |
| 読み込めるもの | CSV、JSON、JSON Lines、または貼り付けたテキスト | CSV、JSON、JSON Lines(コードが読むもの) | Firestore 独自のエクスポート ファイルだけ |
| フィールドのタイプ | フィールドごとに、プレビューを見ながら選ぶ | コードが変換するもの | エクスポートのまま |
| 既存のドキュメント | そのままにする、置き換える、統合する | create、set、merge | 上書きされる |
| 課金の有効化 | 不要 | 不要 | 必要(Blaze プラン) |
1. Firexport で Firebase コンソールからインポート
Firexport は、Firestore のパネルビュー(Panel view)で、各コレクションのドキュメントを追加(Add document)の行の右端にインポート アイコンを追加する無料の Chrome 拡張機能です。接続した Google アカウントで書き込むので、サービス アカウント キーもコードの準備も不要です。
- Chrome ウェブストアから Firexport をインストールします。
- Firebase コンソールで Firestore Database > データ(Data)をパネルビュー(Panel view)で開き、インポート先のコレクションをクリックします。
- ドキュメントを追加(Add document)の行の右端にあるインポート アイコンをクリックします。
- CSV、JSON、JSON Lines のファイルを選ぶかドロップします。またはテキストを貼り付けに切り替えて、読み込むをクリックします。
- 各フィールドのタイプと、ドキュメント ID をどこから取るかを確認し、ドキュメントがすでにある場合の動作を選びます。
- 初回は、ウィンドウから Google アカウントを接続するよう求められます。プロジェクトに書き込めるアカウントを選び、Google のウィンドウでアクセスを許可します。そのあとインポートをクリックします。


フィールドのタイプとドキュメント ID
- CSV のセルはテキストで、JSON にはタイムスタンプも参照もありません。そこで Firexport は、各フィールドの値からタイプを提案します。文字列、整数、数値 (double)、ブール値、タイムスタンプ、参照、ジオポイント、バイト (base64)、またはインポートしない、のいずれかです。提案するのはすべての値が合うときだけで、どれも変更できます。
- タイムスタンプは ISO 8601(
2026-10-01T09:30:00Z)、ブラウザのタイムゾーンでの2026-10-01 09:30:00、またはミリ秒で書けます。参照はusers/u1のようなパスで書けます。マップと配列の中の値は JSON のタイプのままなので、ネストしたデータは JSON に入れてください。CSV の列は常に最上位のフィールド 1 つになります。 - ドキュメント ID は、選んだ列、JSON オブジェクトのキー、または新しいランダム ID から取ります。
Document ID列は自動で選ばれるので、Firexport でエクスポートしたファイルはそのままインポートできます。/users/u1のようにパスで書かれた ID は最後の区切りを使います。 - プレビューには、最初のドキュメントが書き込まれるとおりの形で、各値のタイプとともに表示されます。タイプに合わない値や重複した ID など、書き込めない行は一覧に表示され、何かを送る前に除外されます。
ドキュメントがすでにある場合
デフォルトのそのままにするは、新しいドキュメントだけを作成します。ID がすでにある行は失敗し、一覧に表示されます。置き換えるでは、ドキュメントはファイルにあるフィールドだけになります。統合するは、ファイルのフィールドをマップの中まで書き込み、ドキュメントのほかのフィールドは残します。新しいランダム ID が既存のドキュメントと重なることはありません。

ドキュメント 1 件が Firestore の書き込み 1 回で、プロジェクトに課金されます。Firexport は毎秒最大 500 回の書き込みを送り、一時的な理由で失敗した書き込みは再試行します。すべての行を書き込むと、ウィンドウは短い通知を出して閉じます。失敗した行があれば一覧にし、CSV のレポートを用意し、再開もできます。書き込みは接続したアカウントの権限で行われるので、セキュリティ ルールは適用されません。ファイルはブラウザから直接 Google に送られ、私たちには一切送られません。ファイルと貼り付けたテキストは 100 MB まで扱えます。
スクリプトもサービス アカウント キーもなしで、CSV や JSON のファイルをコンソールから Firestore にインポートしましょう。
2. Admin SDK スクリプトでインポート
100 MB を超えるファイル、スケジュールで実行するインポート、独自の変換には、Firebase Admin SDK を使う Node.js スクリプトで行を直接書き込みます。下のスクリプトは、ヘッダー行つきの CSV、JSON 配列、ドキュメント ID をキーにした JSON オブジェクト、または JSON Lines を読み込み、1 行につき 1 つのドキュメントを書き込みます。Firestore に書き込めるアカウントが必要です。プロジェクトのオーナーか編集者、または Cloud Datastore ユーザーのロールです。
- SDK と CSV パーサーをインストール:
npm install firebase-admin csv-parse - ローカルの認証情報でサインイン:
gcloud auth application-default login。サーバーでは代わりにGOOGLE_APPLICATION_CREDENTIALSでサービス アカウント キーを指定します。 - 下のスクリプトを
import-to-firestore.mjsとして保存し、your-project-idを置き換え、TYPESを自分のフィールドに合わせて、node import-to-firestore.mjs orders.csv ordersを実行します。
// Usage: node import-to-firestore.mjs orders.csv orders → one document per row in orders
// Reads .csv (with a header row), .json (an array, or an object keyed by document ID) and .jsonl.
import { readFileSync } from 'node:fs';
import { initializeApp } from 'firebase-admin/app';
import { getFirestore, Timestamp } from 'firebase-admin/firestore';
import { parse } from 'csv-parse/sync';
initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
const [file, collection] = process.argv.slice(2);
if (!file || !collection) throw new Error('Usage: node import-to-firestore.mjs <file> <collection>');
// The column or key that holds each document's ID. Rows without one get a random ID.
const ID_FIELD = 'Document ID';
// CSV cells are all text and JSON has no timestamps: list the fields to convert. The rest stay as they are.
const TYPES = { createdAt: 'timestamp', items: 'integer', total: 'number' };
// 'create' never touches an existing document (that row fails), 'set' replaces it, 'merge' adds to it.
const MODE = 'create';
const text = readFileSync(file, 'utf8');
const isCsv = file.endsWith('.csv');
let rows;
if (isCsv) {
rows = parse(text, { columns: true, bom: true, skip_empty_lines: true });
} else if (file.endsWith('.jsonl')) {
rows = text.split('\n').filter((line) => line.trim()).map((line) => JSON.parse(line));
} else {
const json = JSON.parse(text);
rows = Array.isArray(json) ? json : Object.entries(json).map(([id, data]) => ({ [ID_FIELD]: id, ...data }));
}
function convert(type, value) {
if (type === 'timestamp') {
const date = new Date(/^\d+$/.test(value) ? Number(value) : value); // ISO 8601 or milliseconds
if (Number.isNaN(date.getTime())) throw new Error(`not a timestamp: ${value}`);
return Timestamp.fromDate(date);
}
if (type === 'integer' || type === 'number') {
const number = Number(value);
if (value === '' || !Number.isFinite(number) || (type === 'integer' && !Number.isInteger(number))) {
throw new Error(`not ${type === 'integer' ? 'an integer' : 'a number'}: ${value}`);
}
return number;
}
if (type === 'boolean') return value === true || value === 'true';
return value;
}
// Convert every row before writing anything, so a bad value stops the import instead of half of it.
const docs = rows.map((row, index) => {
const data = {};
for (const [field, value] of Object.entries(row)) {
if (field === ID_FIELD || (isCsv && value === '')) continue; // an empty CSV cell is a missing field
try {
data[field] = TYPES[field] ? convert(TYPES[field], value) : value;
} catch (error) {
throw new Error(`Row ${index + 1}, ${field}: ${error.message}`);
}
}
const id = row[ID_FIELD] ? String(row[ID_FIELD]).split('/').pop() : undefined; // a path keeps its last part
return { row: index + 1, ref: id ? db.collection(collection).doc(id) : db.collection(collection).doc(), data };
});
// BulkWriter sends the writes in batches, keeps under Firestore's rate limits and retries what can be retried.
const writer = db.bulkWriter();
const failures = [];
for (const { row, ref, data } of docs) {
const write = MODE === 'create' ? writer.create(ref, data) : writer.set(ref, data, { merge: MODE === 'merge' });
write.catch((error) => failures.push(`Row ${row} (${ref.id}): ${error.message}`));
}
await writer.close();
console.log(`Wrote ${docs.length - failures.length} of ${docs.length} documents to ${collection}`);
failures.forEach((failure) => console.log(failure));- CSV のセルはテキストです。スクリプトは
TYPESにあるフィールドだけを変換し、残りは文字列のままにします。空のセルはフィールドなしになります。JSON の値はタイプを保ち、マップと配列はそのまま書き込まれます。ただし、配列の中に直接ある配列は例外で、Firestore が受け付けません。 - 何かを書き込む前にすべての行を変換するので、不正な値があると、インポートを途中まで残すのではなく、その行とフィールドを示してスクリプトが止まります。
MODEが'create'なら、既存のドキュメントには手を付けず、その行は失敗として一覧に出ます。'set'は置き換え、'merge'はファイルのフィールドを追加します。ID のない行は実行のたびに新しいランダム ID になるので、スクリプトを 2 回実行するとそれらの行は重複します。BulkWriterは書き込みをまとめ、毎秒 500 回から始めて Firestore の 500/50/5 ルールが許す範囲で増やし、一時的な理由で失敗した書き込みを再試行します。コレクションにはusers/u1/ordersのようなサブコレクションのパスも指定できます。方法 1 と同じくセキュリティ ルールは適用されず、ドキュメント 1 件が書き込み 1 回です。
3. gcloud のマネージド インポートでデータベース全体をコピー
gcloud firestore import(Google Cloud コンソールの Firestore のインポート/エクスポート ページにもあります)は、Firestore のマネージド インポートです。読むのは gcloud firestore export が Cloud Storage バケットに書き出した Firestore 独自の形式のものだけで、CSV や JSON は読みません。バックアップの復元や、別のプロジェクトやデータベースへコレクションをタイプを保ったままコピーするのに使います。
# 1. Export the collection from the source project.
gcloud firestore export gs://YOUR_BUCKET/orders-export --collection-ids=orders --project=source-project-id
# 2. Import that export into the destination project.
gcloud firestore import gs://YOUR_BUCKET/orders-export --project=destination-project-id- 課金の有効化(Blaze プラン)が必要です。インポートはドキュメントごとに 1 回の書き込みです。
- インポートは同じ ID のドキュメントを上書きし、ほかのドキュメントはそのまま残します。Cloud Functions はトリガーされません。
- アカウントには、オーナー、Cloud Datastore オーナー、または Cloud Datastore インポート / エクスポート管理者のロールが必要です。別のプロジェクトのバケットからインポートするには、インポート先プロジェクトの Firestore サービス エージェントである
service-PROJECT_NUMBER@gcp-sa-firestore.iam.gserviceaccount.comに、そのバケットへのアクセス権が必要です。
FAQ
Firebase コンソールから CSV や JSON のファイルを Firestore にインポートできますか?
いいえ。Firestore のデータタブではドキュメントを追加(Add document)でドキュメントを 1 件ずつ追加するだけで、gcloud firestore import は Firestore 独自のエクスポート ファイルしか読みません。CSV や JSON のファイルを読み込むには、Admin SDK スクリプトか、コンソールにインポートを追加する Firexport のようなブラウザ拡張機能が必要です。
JSON ファイルを Firestore にどうインポートしますか?
ドキュメントごとに 1 つのオブジェクトを持つ JSON 配列か、ドキュメント ID をキーにしたオブジェクトを使います。Firexport では、コレクションのドキュメントを追加(Add document)の行にあるインポート アイコンをクリックしてファイルを選びます。マップと配列は JSON のタイプのままです。Admin SDK では、上のスクリプトのように、ファイルを読み込んで各オブジェクトを BulkWriter で書き込みます。
タイムスタンプや参照はどうインポートされますか?
CSV と JSON は Firestore のタイプを持てないので、ファイルの中のタイムスタンプは、何かが変換するまでただのテキストです。Firexport は、フィールドのタイプをタイムスタンプ、参照、ジオポイントにすると変換します。スクリプトでは、たとえば Timestamp.fromDate(new Date(value)) や db.doc(path) で自分で変換します。
インポートにセキュリティ ルールは適用されますか?
いいえ。Firexport のインポート、Admin SDK、gcloud はどれもアカウントの IAM 権限で書き込み、セキュリティ ルールが適用されるのはクライアント SDK だけです。インポートする前に、データとアカウントを確認してください。
Firestore へのインポートにはどれくらい費用がかかりますか?
どの方法でも、インポートするドキュメント 1 件が書き込み 1 回です。無料の Spark プランでは 1 日 20,000 回の書き込みから差し引かれ、それを超えた書き込みは割り当てがリセットされるまで失敗します。Blaze プランでは、無料枠を超えた書き込みはデータベースのロケーションの料金で課金されます。
