Open cbdeveloper opened 3 years ago
This issue does not have all the information required by the template. Looks like you forgot to fill out some sections. Please update the issue with more information.
@cbdeveloper thanks for the feedback. So just to make sure I understand this correctly, the change you'd like to see is for the Firebase CLI to print a warning when there is access to Firestore using a different project ID than the default?
I have just lost an hour or so to this :sweat_smile: Definitely user error, but a warning would be really, really useful! I think if I hadn't found this issue it would have taken me much longer to realise on my own what was happening. In my case it was quite easy to fall into because I have a few separate firebase projects for my app (dev/staging/prod) - I had my emulator using the dev project ID but my local app was pointing at staging (for reasons best known to the version of me from last week...)
Hey @samtstern , the emulator is run by firebase-tools
, correct? From my experience with this issue, it seems that firebase-tools
only responds to calls made from firebase/app
s that have been initialized to the same project as the currently active one on firebase-tools
. I don't think it's necessarily the default
project on .firebaserc
, if that's what you meant. But we would have to test it to confirm it.
firebase-tools
probably already knows somehow that the call to the emulator is being made from a specific initialized project X
from client code. If that project X
is different than the one that is being used on the firestore emulator (which is probably the one that is active when you list firebase projects:list
), it should display some kind of warning/error message, given the fact that in this situation (from my experience) it will only serve the initially loaded data from the emulator and the client code won't see any updates made to the data.
@itsravenous experience seems to confirm the issue described here. Like me, he also has multiple firebase projects configured for multiple environments.
@cbdeveloper yes that's correct, the emulator suite emulates the currently active project as determined by firebase-tools
. That's the project which will be shown in the Emulator UI and that's also the project which will be used when configuring Cloud Functions triggers locally.
The emulators are actually run as a "swarm" of local processes and they accept requests directly, requests are not proxied through firebase-tools
. So to implement this type of warning we'd have to make a change to each individual emulator (Firestore, Realtime Database, etc). Not impossible but definitely a lot of work! We'd also have to find some way to disable this warning for unit testing because many developers use randomly generated project IDs to create isolated unit tests.
@samtstern thanks for your reply. If it's not feasible to implement it, I guess a warning about this issue should be added to the documentation. Maybe on this section:
https://firebase.google.com/docs/emulator-suite/install_and_configure
Given that:
process.env.xxx
or useEmulator
)Why can't we completely ignore the project ID in emulator mode?
* this is an assumption based on my experience that the Emulator UI & functions triggers don't seem to work if the projectIds don't line up, but I might be wrong
@charles-allen what do you mean by "completely ignore the project ID"?
@charles-allen what do you mean by "completely ignore the project ID"?
For me, I only connect to one project, and I want everything connected to that project: emulated functions, emulated firestore, emulated auth, emulator UI, admin sdk, client sdk.
I'm already required to explicitly initialize the SDKs in emulator mode:
// For the Admin SDK:
if (useEmulator) {
...
process.env.FIREBASE_AUTH_EMULATOR_HOST = `localhost:${firebaseJson.emulators.auth.port}`
process.env.FIRESTORE_EMULATOR_HOST = `localhost:${firebaseJson.emulators.firestore.port}`
}
- OR -
// For the Client SDK:
if (useEmulator) {
auth.useEmulator(`http://localhost:${EMU_PORT_AUTH}/`)
db.useEmulator('localhost', parseInt(EMU_PORT_FIRESTORE))
functions.useEmulator('localhost', parseInt(EMU_PORT_FUNCTIONS))
}
And by default the emulator automatically takes the active project from firebase use
; so from my perspective, my calls could just be directed to that project, irrespective of my config, given that I've already made it clear that I want to use the emulator.
I suspect that this logic fails because my first assumption is wrong, that the emulator can actually support multiple projects, and that (unlike me) some people connect to multiple databases without caring that the Emulator UI (and possibly functions) are not available.
@charles-allen I agree this is super confusing and there's a fundamental mismatch at the center which makes it tricky to solve.
(1) The individual service emulators (Firestore, RTDB, Functions, etc) which make up the Emulator Suite run as their own processes and emulate the production services. In most cases the Project ID is part of the request and just like the production service these emulators are happy to process requests for different projects! So if you write to the Firestore emulator with 100 different project IDs it will just keep track of 100 different databases.
(2) The "emulator suite" is what we call the controller in the CLI that helps manage this swarm of emulators. So it's the one that does things like register functions triggers. Unlike the individual services it has a strong notion of a project. So when it wires up Cloud Functions triggers, for instance, it does them under a single project ID.
(3) Despite the fact that the CLI is running the show, the SDKs still communicate directly with each service emulator. So there's no chance for us to intercept the request and say "that doesn't look like the right project ID".
And you might say "well (1) is really dumb behavior!" and in most cases you'd be right but when people write unit tests for the RTDB/Firestore emulators it's a very common pattern to use randomized project IDs to segregate data between test runs. In hindsight this is probably something we shouldn't have allowed but we can't break it now.
So hopefully that's a useful explanation! I do still hope we can do something to alleviate this confusion (besides just make the docs better) but we have to find a way that's not a breaking change.
It definitely makes more sense once you know a bit about how the emulator works :) Thanks for your help!
Some feedback based on my experience:
--project
is not well-documented as yetprocess.env.FUNCTIONS_EMULATOR
to detect that we're running in emulator mode; it might be nice to also set process.env.GCLOUD_PROJECT
to the projectId the emulator is using to make it more transparent that it silently picks up the active firebase use
ID. Also I believe explicitly setting a project id using admin.initialize({ projectId: 'fake' })
is silently ignored.@charles-allen thank you for the detailed feedback! Those are all good points and I will share them with the team. There's one I want to comment on:
I think it's non-intuitive for the emulator to default to a production project ID
There are two main reasons why we chose to go this route rather than using some fake project id "foo-bar-baz-123":
useEmulator()
calls (which we can't really avoid). By using a production project ID you can keep your initializeApp()
block unchanged.
[REQUIRED] Environment info
firebase-tools: @8.16.2
Platform: Windows 10
[REQUIRED] Test case
I've described the best I could. I hope it helps to improve the package.
[REQUIRED] Steps to reproduce
I was getting strange behaviors from firebase Firestore emulator. It seems that the problem was that
firebase-tools
had a different selected project as from the project that was being initialized by my client code with:firebase.initializeApp(config);
.What I mean is:
firebase.initializeApp(config)
was pointing to a.env
file that had the config for aproject-old
keys..env
And
firebase-tools
was set to:project-new
. So, when I ranfirebase projects:list
, this is what I was getting:[REQUIRED] Expected behavior
I was expecting to get some warning about that difference between projects.
[REQUIRED] Actual behavior
My client code was able to read data from Firestore emulator. But only the initial data. The one that was initialized with the emulator. It was unable to read any new data from the Firestore. For example: I would go to the emulators UI on
localhost:4000
and update some data. Even after refreshing my app, the app would still read theinitialData
and not the updated data. Also, no real timeonSnapshot
listeners were activated.Ideally, I think that
firebase-tools
should be aware that its emulator is being called by a different firebase app, that was initialized on another project, and would show some kind of warning or error.I feel that the emulator was failing silently and it was hard to find out what was wrong. No warnings or errors were being shown.
When I updated my client code to
initializeApp(config)
to theproject-new
keys, everything started to work fine again.This is how I'm initializing the emulators:
This will initialize the emulators with the data from
./src/firebase/emulatorData
And on my client code I have:
firebase.ts