Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
253 views
in Technique[技术] by (71.8m points)

node.js - Connect to both Firestore online and emulator at the same time?

I'd like to write a CLI script that copies some data from our live app to a local firestore emulator, for development and testing purposes.

The docs say that you should set the FIRESTORE_EMULATOR_HOST environment variable to connect to a local emulator -- but that doesn't allow the same app to connect to the server as well.

I've tried this:

const onlineApp = await admin.initializeApp(
    {
      credential: admin.credential.applicationDefault(),
      databaseURL: 'https://....firebaseio.com',
    },
    'default'
  );

process.env['FIRESTORE_EMULATOR_HOST'] = 'localhost:8080';

const emulator = await admin.initializeApp(
    {
      projectId: 'emulator',
    },
    'emulator'
  );

... but then onlineApp no longer talks to the online Firestore.

Is there another way to do this so I can create a connection to both at the same time?

question from:https://stackoverflow.com/questions/65909321/connect-to-both-firestore-online-and-emulator-at-the-same-time

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After some trial and error it seems to be possible to connect to both simultaneously. The code below connects first to the real Firestore database and makes the document reads needed, then instantiates a second client to talk with the Firestore emulator and make the writes.

const admin = require('firebase-admin');
const serviceAccount = require('./key.json');

const f1 = async () => {
    const onlineApp = admin.initializeApp(
        {
            project: "PROJECT_ID",
            credential: admin.credential.cert(serviceAccount)
        },
        'default'
    );
    const onlineFirestore = onlineApp.firestore()
    const ondoc = await onlineFirestore.collection("XXX").doc("YYY").get()
        .then((doc) => {
            console.log(doc.data())
            return doc.data()
        });
    // Retrieve whatever needed here
}

const f2 = async () => {
    const emulator = admin.initializeApp(
        {
            project: "PROJECT_ID",
            credential: admin.credential.cert(serviceAccount)
        },
        'emulator'
    );
    process.env['FIRESTORE_EMULATOR_HOST'] = 'localhost:8080';
    const emulatorFirestore = emulator.firestore()
    await emulatorFirestore.collection("XXX").doc("YYY").set({some:"thing").then((res) => console.log(res));
}

const flow = async () => {
    await f1();
    await f2();
}

flow()

I'm unable to explain exactly why the code above works (I'll update the answer if I find out more). Nonetheless here are some things I'd like to mention.

  • The Firestore client automatically reads the environment variable FIRESTORE_EMULATOR_HOST. It seems that you can force the first to connect to the online one by making a read before setting the environment variable.
  • If the FIRESTORE_EMULATOR_HOST is already set in the system before executing the script it just fails. As before, the client reads it by itself which makes the onlineApp connect to the emulator. To prevent so, make sure to stop-restart the emulators before the script runs.
  • All the async/await stuff is there to force the evaluation order for the reasons mentioned above.

All that said, I would rather suggest to make an export of the production Firestore instance and use it in the emulator, which seems more reliable. Also, keep in mind that the emulator has an import/export functionality which allows to preserve the database state across emulator runs, thus it may be easier to set up the data once (manually or from plain JS objects) and export it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...