A Firestore export must define which collections and subcollections it intends to walk. These older scripts used a hand-written schema to describe that tree. They do not automatically discover every collection.
Historical, incomplete scripts. The source is retained for understanding the traversal approach. It has not been executed, and the defects below prevent treating it as a ready-to-run backup or migration tool.
Copy the named collection tree
const firebase = require('firebase-admin');
const serviceAccountSource = require("./source.json"); // source DB key
const serviceAccountDestination = require("./destination.json"); // destiny DB key
const sourceAdmin = firebase.initializeApp({
credential: firebase.credential.cert(serviceAccountSource)
});
const destinyAdmin = firebase.initializeApp({
credential: firebase.credential.cert(serviceAccountDestination)
}, "destination");
/**
* This schema is how your DB is organized in a tree structure. You don't have to care about the Documents
* but you do need to inform the name of your collections and any sub collections, in this
* case we have two collections called users and groups, the all have their documents, but
* the collection users has its own sub collections, friends and groups, which again have their
* own sub collection, messages.
* @type {{groups: {}, users: {groups: {messages: {}}, friends: {messages: {}}}}}
*/
const schema = {
users: {
friends: {
messages: {},
},
groups: {
messages: {},
},
},
groups: {},
};
const source = sourceAdmin.firestore();
const destination = destinationAdmin.firestore();
const copy = (sourceDBrep, destinationDBref, aux) => {
return Promise.all(Object.keys(aux).map((collection) => {
return sourceDBrep.collection(collection).get()
.then((data) => {
let promises = [];
data.forEach((doc) => {
const data = doc.data();
promises.push(
destinationDBref.collection(collection).doc(doc.id).set(data).then((data) => {
return copy(sourceDBrep.collection(collection).doc(doc.id),
destinationDBref.collection(collection).doc(doc.id),
aux[collection])
})
);
});
return Promise.all(promises);
})
}));
};
copy(source, destination, aux).then(() => {
console.log('copied');
});
The source initializes destinyAdmin but later reads destinationAdmin. Its final call also passes aux without defining it in that listing. Those names are preserved so the article does not silently claim a corrected or verified migration.
The writes use the original document IDs, but that alone does not establish completeness, recovery, overwrite safety, or a consistent snapshot.
Inspect the export shape
const admin = require('firebase-admin');
const serviceAccount = require("./key.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
const dumped = {};
const schema = {
checkin: {},
config: {},
sections: {},
voters: {}
};
var db = admin.firestore();
const dump = (dbRef, aux, curr) => {
return Promise.all(Object.keys(aux).map((collection) => {
return dbRef.collection(collection).get()
.then((data) => {
let promises = [];
data.forEach((doc) => {
const data = doc.data();
if (!curr[collection]) {
curr[collection] = {
data: {},
type: 'collection',
};
curr[collection].data[doc.id] = {
data,
type: 'document',
}
} else {
curr[collection].data[doc.id] = data;
}
promises.push(dump(dbRef.collection(collection).doc(doc.id), aux[collection], curr[collection].data[doc.id]));
});
return Promise.all(promises);
});
})).then(() => {
return curr;
})
};
let aux = {...schema};
let answer = {};
dump(db, aux, answer).then((answer) => {
console.log(JSON.stringify(answer, null, 4));
});
The export’s first document and subsequent documents are represented differently by its two assignment branches. Review that output model before relying on a consumer to restore it. Collections absent from the schema are outside the traversal.
The original usage note
## Install lib
```
npm install firebase-admin
```
## Update keys file
## Update schema
## How to use
```
node exportFirestoreDB.js > firestore_data_bk.json
```
The referenced JSON key files are local service-account credentials; their contents are not included. No database was accessed or copied for this revision.