Check out a free preview of the full Firebase Fundamentals course
The "Setup Local Emulator Environment" Lesson is part of the full, Firebase Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
David walks through connecting to the Auth and Firestore emulators, the contents of the firebase.json file, and enabling the Emulator UI. Student questions regarding how often the offline data store updates and caches data and if users and data can be populated in a development environment are also covered in this segment.
Transcript from the "Setup Local Emulator Environment" Lesson
[00:00:00]
>> We talked about, sorry, Firebase updating optimistically or the like of. I'm just kind of curious, is there, like you mentioned offline, is the datastore offline that then syncs whenever possible, or what happens if you-
>> Yeah.
>> Update and then the user closes the tab, right, and they come back to that tab, is that data still there?
[00:00:24]
>> Yeah, and when we get to the very next sections, Firestore, we're gonna get a lot more into that, but that's a very good question to be curious about right now. So yes, and that's a lot of the reason why those writes can happen without awaiting them, cuz they are themselves not dependent on the network state.
[00:00:41]
When you write that, it will create an entry, what we call a pending write, and then there's a whole queue of writes that gets sent off to a server behind the scenes. But all of that gets synchronized in a cache in IndexedDB. And so you have to enable offline, which we'll see, but if you have offline enabled and then you run that command, it write a setDoc or something like that, it enters that in this background queue.
[00:01:08]
And if the user just closed the tab before it happened, that's fine, it's sitting in IndexedDB for the next time they open back up again. It just says, oop, is there any pending writes left in the queue? There are, let me take off from there, yeah. And there's information that we see as well within snapshots about the state of that data, whether it is appending write or whether it's from the cache or from the network as well.
[00:01:33]
So you can build a lot of really powerful offline UI with the cloud with a cross through, you can say, hey, this one hasn't synced yet. Okay, so now this is a nice method, because it has everything initialized, cuz it's everything that we need. And if we're using more services, we can just add more and more into it.
[00:01:54]
And everything kinda comes back through initialize. And cool thing about initialize is that initialize app, as long as you pass through the same configuration object, you can call it over and over again. And it notices that and says, hey, I'm not gonna reinitialize an app for you, I'm just gonna give you the one I know already exist.
[00:02:12]
So what I want to do at this point though is I have everything together, so I want to connect out to emulators. And each emulator supporting service has what we call the connect API, because they all start with connect, so connect to Firestore emulator, Connect to Auth emulator.
[00:02:31]
And what I can do within here is to say, okay, let's connect to AuthEmulator, I'll pass on the auth object. I need to say this runs on localhost, what is it, 999. We're gonna set this up in just a minute, I'm doing a lot of this from memory.
[00:02:53]
And then the same thing with Firestore, they're a little different in their signature. So I pass in Firestore, the host is local host, the port number usually is 8080, and I think that's all I need. So now, what this will do is it'll say, hey, let me connect out to the emulators.
[00:03:12]
And I can even say if location.hostname is equal to localhost, Then do this. So I'm not accidentally trying to connect out to emulators while in production. There are more advanced ways of actually just removing this with Webpack and other cool build tools. But this is sort of a very simple approach of saying, hey, one of my local hosts, let me connect out to emulators, otherwise, let me connect out to production services.
[00:03:48]
So to get the emulators up and running, you can know if you have emulators set up because firebase.json is sort of a manifest of all these things that you're running in Firebase. This is the hosting key cuz we set up our hosting. We set our public folder was dist, we automatically wanna ignore node modules and stuff like that.
[00:04:08]
And then this rewrite right here sets up a rewrite for a single page app, so they always go home and do client side routing instead. So what we also wanna do is we wanna set up emulators, and you can see that the emulators would be configured in here, and we don't.
[00:04:25]
So what we can say is npx firebase init emulators. And when this runs, it says, o,h great, what emulators would you like to set up? Well, I want the authentication emulator, and I want the Firestore emulator. I hit Enter and it's gonna say, hey, what ports do you want?
[00:04:44]
And if you look at that, auth runs on 999, that's our default one, I always use that one, you can use which one you would like. Firestore runs an 8080. And in hosting, I like to never run things on port 5000, because everything runs on port 5000. So I don't know, 5032, that sounds good.
[00:05:02]
And then it says, would you like to enable the Emulator UI? Which yes, it's awesome, and we're gonna leave the Emulator UI port empty, so it connects to whatever available port is there. And if you need to download the emulators there, it will do it, and then now I can connect out to emulators.
[00:05:23]
And so what I can do now is I can actually take a look and run at the emulators. So I can say npx firebase, man, I wish this was a little bigger, I can say npx firebase emulators start. And it's gonna run. And this is why we need Java, because these emulators, they expect a Java runtime.
[00:05:50]
And so it goes, awesome. Look at that, I am up and running, I have an authentication emulator and Firestore and also hosting. And if we go into local host 4000, we can see that this is a local running UI that allows us to manage all of these things offline.
[00:06:11]
There's a lot of other emulators as well, but with auth, we can add users. With Firestore, we have a very similar data viewer, we can start adding or seeding in data, and we're doing all of this locally. And so at no point do you have to worry about, oops, I'm writing this out to production.
[00:06:33]
All right, so back to the config. So now, if I go into my components, let's go into here, instead of calling initializeApp, I can just import initialize from./firebase And I don't even need a config anymore cuz it's doing all that behind the scenes. And that's gonna import from this firebaseApp.
[00:07:06]
Also, firestore and auth will return from that. I can delete auth from there, and I don't even think I need, yes, I do need auth right here. And so I can press that in to sign in anonymously. And we could have everything working there, but this will actually work from their local emulator.
[00:07:31]
So I'm running the emulator in one tab right here, and so I can go and run my dev server in another. So this will be on 3001, it's ../firebase, I believe, my bad, awesome. So this right here says running in emulator mode. Do not use with production credential.
[00:07:54]
So we know we're connected out to the emulator. And this is kind of an annoying little banner, and when most people, you set up the emulator, they ask how do I get rid of that banner? And to get rid of that banner, it's pretty easy, you just come into the connect.
[00:08:08]
And we have a configuration object as the last parameter where you can say disable, I do not need all of that VS Code, disableWarnings is true. And then now, if I refresh, look at that, it's gone, but we are connected out to the emulator. If I click sign in, we got signed in.
[00:08:30]
And if I come out to authentication, look at that, this is right in my local emulator. So the same thing can happen with Firestore, if I start writing data in Firestore, we'll see everything happen there. For the sake of time, I wanna get to the other stuff, so I'm gonna move forward without connecting out to, actually, yeah, to Firestore.
[00:08:53]
But one of the things I'm going to show now is you can also, where we talked about deploying, every time I deployed, it went to markdown-me.web.app. So let's say someone was like, hey, let's add a new feature, and I deployed it, well, if I deploy it to production and it breaks, that's not great.
[00:09:10]
I want some sort of collaborative environment for doing deployments. So I can use a feature of Firebase hosting called preview channels. And so I could say npx firebase hosting:channel:deploy, and I can give it any name I want, staging. And so what this will need to do is it'll need to be the built version, which I may have needed to run build, but that's fine.
[00:09:36]
So then it's gonna go out, it's gonna do a deployment. But instead of going out to a specific URL, I'm using a different project at this point. But as you can see, it goes out to this URL, which sort of gives me some generated URL. And what's great about this is that I have a new change, I wanna send it to a coworker, to a friend, or whoever to test it out for me, I just send out this preview channel, I give it to someone and they can use it.
[00:10:06]
And the default setting for preview channels is in seven days, they just disappear. So you can configure that to be shorter or longer. And also with Firebase CLI, you can set this to work up with GitHub actions. So out of the box, every time someone sends a pull request, it will create a preview channel.
[00:10:26]
And every time a commit happens to that pull request, it will rerun and deploy the latest changes. So it's a really nice way of doing PRs and doing collaborative like, hey, I changed background to blue. Wait, no, change it to cyan or whatever, just really nice way of seeing different changes.
[00:10:45]
So yeah, so that right there allows me to have local environments and staged environments. And so this keeps us from just pushing all changes to one project with all these cloud connected environments. So that is a bit of a best practice. But also a great part about having the local emulators is that what we saw here in the console is that our security rules say, hey, anyone can read and write data at this location before this date.
[00:11:15]
That's not great, I don't like that, I don't want people to be able to read and write. So what I actually can do for now since I'm in development mode, I can say, hey, allowed read and write is false. And so now, no one is going to be able to read and write, so this database will fail for everyone.
[00:11:31]
And what I can do is locally, I can create a local set of rules. I'll go back to the console just for you, Mark. And I can say npx firebase init firestore, and then now it's going to set up local Firestore rules for me, indexes which we'll get into.
[00:11:53]
And then now, I have this new firestore.rules file, and I can develop all of my security rules locally, and they work against the emulator as well. So any changes I make, which we'll get all into security rules here in a minute, to these security rules, I can make sure, if there's a bug, I will find it locally before I find it in production.
[00:12:16]
And if my production data is not ready to be read yet, I can lock it all down and then just keep running off this local environment. So, yes.
>> Can I populate users and data in my development environment, so I don't have to recreate them?
>> Absolutely, so one of the things that we'll be using a lot throughout this whole section is, so Firebase has a server SDK, we call the Admin SDK that is very similar to the client SDKs.
[00:12:45]
But it has a lot more powerful actions, because it's some server, it can do a lot. You can use that to write data if you wanna do your own scripting. And with the emulators, you can write a command where you say firebase emulators:start, and you can say import=./seed or local.
[00:13:12]
And then also one of my favorite bits is you can say export-on-exit, and when you turn off the terminal, whatever existed gets spit out into that .local file. So you can seed it all up one time with some commands or you can manually enter it. And when it cuts out, it will export it all out and you won't have to do a receipt every time, it just reads off of that file or set of files.
[00:13:39]
And it does the same for the Auth as well, you'll see it'll spit out all of your user records. So I use that totally. And actually as we get into some of the more exercises, I'll point out to you where they are, so you see, yeah, I'm reading in from that section.
[00:13:52]
So a great question. Awesome, so this is a bit of a better practice than what we were doing before. We're connecting out to emulators, we're deploying to different environments, we are not talking to production. And so we don't have to worry about security from the get go, is someone gonna be talking to my database and all that?
[00:14:11]
Nope, we can work all locally until we're ready to get to production.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops