apps/docs/content/guides/platform/migrating-to-supabase/firestore-data.mdx
Supabase provides several tools to convert data from a Firebase Firestore database to a Supabase Postgres database. The process copies the entire contents of a single Firestore collection to a single Postgres table.
The Firestore collection is "flattened" and converted to a table with basic columns of one of the following types: text, numeric, boolean, or jsonb. If your structure is more complex, you can write a program to split the newly-created json file into multiple, related tables before you import your json file(s) to Supabase.
Clone the firebase-to-supabase repository:
git clone https://github.com/supabase-community/firebase-to-supabase.git
In the /firestore directory, create a file named supabase-service.json with the following contents:
{
"host": "database.server.com",
"password": "secretpassword",
"user": "postgres",
"database": "postgres",
"port": 5432
}
On your project dashboard, click Connect
Under the Session pooler, click on the View parameters under the connect string. Replace the Host and User fields with the values shown.
Enter the password you used when you created your Supabase project in the password entry in the supabase-service.json file.
firebase-service.json.node collections.js
node firestore2json.js <collectionName> [<batchSize>] [<limit>]
batchSize (optional) defaults to 1000<collectionName>.jsonlimit (optional) defaults to 0 (no limit)You can customize the way your JSON file is written using a custom hook. A common use for this is to "flatten" the JSON file, or to split nested data into separate, related database tables. For example, you could take a Firestore document that looks like this:
[{ "user": "mark", "score": 100, "items": ["hammer", "nail", "glue"] }]
And split it into two files (one table for users and one table for items):
[{ "user": "mark", "score": 100 }]
[
{ "user": "mark", "item": "hammer" },
{ "user": "mark", "item": "nail" },
{ "user": "mark", "item": "glue" }
]
node json2supabase.js <path_to_json_file> [<primary_key_strategy>] [<primary_key_name>]
<path_to_json_file> The full path of the file you created in the previous step (Dump Firestore collection to JSON file ), such as ./my_collection.json[<primary_key_strategy>] (optional) Is one of:
none (default) No primary key is added to the table.smallserial Creates a key using (id SMALLSERIAL PRIMARY KEY) (autoincrementing 2-byte integer).serial Creates a key using (id SERIAL PRIMARY KEY) (autoincrementing 4-byte integer).bigserial Creates a key using (id BIGSERIAL PRIMARY KEY) (autoincrementing 8-byte integer).uuid Creates a key using (id UUID PRIMARY KEY DEFAULT gen_random_uuid()) (randomly generated UUID).firestore_id Creates a key using (id TEXT PRIMARY KEY) (uses existing firestore_id random text as key).[<primary_key_name>] (optional) Name of primary key. Defaults to "id".Hooks are used to customize the process of exporting a collection of Firestore documents to JSON. They can be used for:
.js file for your collectionIf your Firestore collection is called users, create a file called users.js in the current folder.
.js fileThe basic format of a hook file looks like this:
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
// modify the doc here
return doc
}
collectionName: The name of the collection you are processing.doc: The current document (JSON object) being processed.recordCounters: An internal object that keeps track of how many records have been processed in each collection.writeRecord: This function automatically handles the process of writing data to other JSON files (useful for "flatting" your document into separate JSON files to be written to separate database tables). writeRecord takes the following parameters:
name: Name of the JSON file to write to.doc: The document to write to the file.recordCounters: The same recordCounters object that was passed to this hook (just passes it on).module.exports = (collectionName, doc, recordCounters, writeRecord) => {
doc.unique_key = recordCounter[collectionName] + 1
return doc
}
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
doc.dump_time = new Date().toISOString()
return doc
}
Flatten the users collection into separate files:
[
{
"uid": "abc123",
"name": "mark",
"score": 100,
"weapons": ["toothpick", "needle", "rock"]
},
{
"uid": "xyz789",
"name": "chuck",
"score": 9999999,
"weapons": ["hand", "foot", "head"]
}
]
The users.js hook file:
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
for (let i = 0; i < doc.weapons.length; i++) {
const weapon = {
uid: doc.uid,
weapon: doc.weapons[i],
}
writeRecord('weapons', weapon, recordCounters)
}
delete doc.weapons // moved to separate file
return doc
}
The result is two separate JSON files:
[
{ "uid": "abc123", "name": "mark", "score": 100 },
{ "uid": "xyz789", "name": "chuck", "score": 9999999 }
]
[
{ "uid": "abc123", "weapon": "toothpick" },
{ "uid": "abc123", "weapon": "needle" },
{ "uid": "abc123", "weapon": "rock" },
{ "uid": "xyz789", "weapon": "hand" },
{ "uid": "xyz789", "weapon": "foot" },
{ "uid": "xyz789", "weapon": "head" }
]
Contact us if you need more help migrating your project.