Skip to content
Platform

从 Firebase Firestore 迁移到 Supabase

Migrate your Firebase Firestore database to a Supabase Postgres database.

Supabase 提供了几种 工具 来将数据从 Firebase Firestore 数据库转换到 Supabase Postgres 数据库。这个过程会将单个 Firestore collection 的全部内容复制到单个 Postgres table

🌐 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.

Firestore 的 collection 会被“扁平化”,并转换成一个包含基本列的表,这些列的类型可以是 textnumericbooleanjsonb。如果你的结构比较复杂,你可以写一个程序,把新创建的 json 文件拆分成多个相关的表,然后再将你的 json 文件导入到 Supabase。

🌐 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.

设置迁移工具 #

🌐 Set up the migration tool [#set-up-migration-tool]

  1. 克隆 firebase-to-supabase 仓库:

    1
    git clone https://github.com/supabase-community/firebase-to-supabase.git
  2. /firestore 目录下,创建一个名为 supabase-service.json 的文件,内容如下:

    1
    {
    2
    "host": "database.server.com",
    3
    "password": "secretpassword",
    4
    "user": "postgres",
    5
    "database": "postgres",
    6
    "port": 5432
    7
    }
  3. 在你的项目仪表板上,点击 连接

  4. 在会话池下,点击连接字符串下的“查看参数”。将 HostUser 字段替换为显示的值。

  5. supabase-service.json 文件的 password 项中输入你创建 Supabase 项目时使用的密码。

生成一个 Firebase 私钥 #

🌐 Generate a Firebase private key [#generate-firebase-private-key]

  1. 登录你的 Firebase 控制台 并打开你的项目。
  2. 点击侧边栏 项目概览 旁边的齿轮图标,然后选择 项目设置
  3. 点击 服务账户,然后选择 Firebase 管理 SDK
  4. 点击 生成新的私钥
  5. 把下载的文件重命名为 firebase-service.json

命令行选项 #

🌐 Command line options

列出所有 Firestore 集合 #

🌐 List all Firestore collections

node collections.js

把 Firestore 集合导出到 JSON 文件 #

🌐 Dump Firestore collection to JSON file

node firestore2json.js <collectionName> [<batchSize>] [<limit>]

  • batchSize(可选)默认为1000
  • 输出文件名是 <collectionName>.json
  • limit(可选)默认为 0(无限制)

用钩子自定义 JSON 文件 #

🌐 Customize the JSON file with hooks

你可以使用自定义钩子来自定义 JSON 文件的写入方式。一个常见的用法是“扁平化” JSON 文件,或者将嵌套数据拆分成单独的相关数据库表。例如,你可以把一个看起来像这样的 Firestore 文档:

🌐 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:

1
[{ "user": "mark", "score": 100, "items": ["hammer", "nail", "glue"] }]

然后把它分成两个文件(一个用户表,一个项目表):

🌐 And split it into two files (one table for users and one table for items):

1
[{ "user": "mark", "score": 100 }]
1
[
2
{ "user": "mark", "item": "hammer" },
3
{ "user": "mark", "item": "nail" },
4
{ "user": "mark", "item": "glue" }
5
]

将 JSON 文件导入 Supabase (Postgres) #

🌐 Import JSON file to Supabase (Postgres) [#import-to-supabase]

node json2supabase.js <path_to_json_file> [<primary_key_strategy>] [<primary_key_name>]

  • <path_to_json_file> 你在上一步创建的文件的完整路径(Dump Firestore collection to JSON file ),例如 ./my_collection.json
  • [<primary_key_strategy>](可选)是以下之一:
    • none(默认)表中未添加主键。
    • smallserial 使用 (id SMALLSERIAL PRIMARY KEY)(自增的 2 字节整数)创建一个键。
    • serial 使用 (id SERIAL PRIMARY KEY)(自增4字节整数)创建一个键。
    • bigserial 使用 (id BIGSERIAL PRIMARY KEY)(自增 8 字节整数)创建一个键。
    • uuid 使用 (id UUID PRIMARY KEY DEFAULT gen_random_uuid())(随机生成的 UUID)创建一个密钥。
    • firestore_id 使用 (id TEXT PRIMARY KEY) 创建一个密钥(使用现有的 firestore_id 随机文本作为密钥)。
  • [<primary_key_name>](可选)主键的名称。默认为“id”。

自定义钩子 #

🌐 Custom hooks

Hooks 用来定制将一组 Firestore 文档导出为 JSON 的过程。它们可以用来做以下事情:

🌐 Hooks are used to customize the process of exporting a collection of Firestore documents to JSON. They can be used for:

  • 自定义或修改按键
  • 正在计算数据
  • 把嵌套文档展平成相关的SQL表

写一个自定义 Hook #

🌐 Write a custom hook

为你的收藏创建一个 .js#

🌐 Create a .js file for your collection

如果你的 Firestore 集合叫 users,就在当前文件夹里创建一个叫 users.js 的文件。

🌐 If your Firestore collection is called users, create a file called users.js in the current folder.

构建你的 .js#

🌐 Construct your .js file

钩子文件的基本格式看起来是这样的:

🌐 The basic format of a hook file looks like this:

1
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
2
// modify the doc here
3
return doc
4
}
参数 [#parameters]

🌐 Parameters

  • collectionName:你正在处理的集合名称。
  • doc:正在处理的当前文档(JSON 对象)。
  • recordCounters:一个内部对象,用来记录每个集合中已经处理了多少条记录。
  • writeRecord:这个函数会自动处理将数据写入其他 JSON 文件的过程(对于把你的文档“平铺”成可以写入不同数据库表的独立 JSON 文件非常有用)。writeRecord 接受以下参数:
    • name:要写入的 JSON 文件名。
    • doc:要写入文件的文档。
    • recordCounters:同一个传给这个钩子的 recordCounters 对象(会传递下去)。

示例 #

🌐 Examples

向集合中添加一个新的(唯一的)数字键 #

🌐 Add a new (unique) numeric key to a collection

1
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
2
doc.unique_key = recordCounter[collectionName] + 1
3
return doc
4
}

添加这个记录从 Firestore 导出的时间戳 #

🌐 Add a timestamp of when this record was dumped from Firestore

1
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
2
doc.dump_time = new Date().toISOString()
3
return doc
4
}

把 JSON 展平成单独的文件 #

🌐 Flatten JSON into separate files

users 集合拆成单独的文件:

🌐 Flatten the users collection into separate files:

1
[
2
{
3
"uid": "abc123",
4
"name": "mark",
5
"score": 100,
6
"weapons": ["toothpick", "needle", "rock"]
7
},
8
{
9
"uid": "xyz789",
10
"name": "chuck",
11
"score": 9999999,
12
"weapons": ["hand", "foot", "head"]
13
}
14
]

users.js 钩子文件:

🌐 The users.js hook file:

1
module.exports = (collectionName, doc, recordCounters, writeRecord) => {
2
for (let i = 0; i < doc.weapons.length; i++) {
3
const weapon = {
4
uid: doc.uid,
5
weapon: doc.weapons[i],
6
}
7
writeRecord('weapons', weapon, recordCounters)
8
}
9
delete doc.weapons // moved to separate file
10
return doc
11
}

结果是两个单独的 JSON 文件:

🌐 The result is two separate JSON files:

1
[
2
{ "uid": "abc123", "name": "mark", "score": 100 },
3
{ "uid": "xyz789", "name": "chuck", "score": 9999999 }
4
]
1
[
2
{ "uid": "abc123", "weapon": "toothpick" },
3
{ "uid": "abc123", "weapon": "needle" },
4
{ "uid": "abc123", "weapon": "rock" },
5
{ "uid": "xyz789", "weapon": "hand" },
6
{ "uid": "xyz789", "weapon": "foot" },
7
{ "uid": "xyz789", "weapon": "head" }
8
]

资源 #

🌐 Resources

迁移到 Supabase #

🌐 Migrate to Supabase

如果你在迁移项目时需要更多帮助,联系我们