[MUSIC PLAYING] ANDREA WU: Hi, everyone. I'm Andrea from
the Firebase team. And today, we're going to
look at how to get started with Realtime Database on web. Why would you want to
use Realtime Database in the first place? Well, you can use it to store
and sync data using a NoSQL Cloud database. Data is stored as JSON,
synced across all clients in near real time, and
remains available when your app goes offline. The best part of
all this is it can be done with just a
few lines of code, and Firebase manages the
infrastructure of the database system so you don't
have to spend time worrying about
building, maintaining, and hosting your own database. With that, let's go ahead
and see how we would do this. There are many ways to include
a library in a web app. But in this video, we'll
be using NPM and webpack. If you want more information
on how to set that up with Firebase, check
out this other "Firebase Fundamental" video. Let's get started. First off, let's go
to Firebase Console and navigate to
Realtime Database. Clicking on Create
Database will open a window where we'll pick a location
for our Realtime Database. The default is
usually fine as it's the project's default Google
Cloud platform resource location. If you're not able
to pick a location, then your project already has
a default GCP resource location that was set either
during project creation or when setting up
another service, like Storage, that requires
a location setting. We can then click Next to
pick our security rules. The default security rules
deny all reads and writes from our apps, which probably
wouldn't be very helpful right now since we wouldn't be able
to change the database at all. So we can go ahead and
choose to Start in test mode so we can actually write
data to our database. And we can come back to
update these security rules when we're ready. Then click Enable
here, which will then show our empty database. What are we seeing here exactly? Well, to understand
what this is, we'll have to talk
a bit about how Realtime Database stores data. The tl;dr is that
it's a JSON tree. And when you add data
to the JSON tree, it becomes a node in the
existing JSON structure with an associated key. You can provide your
own keys, like user IDs or semantic names, or Firebase
can generate them for you. What we see here in the Console
is the root of the tree. And when we add
data to it, we'll see the tree-like
structure grow from here as we'll see in a bit. Even though this is a
tree and Realtime Database does allow nesting of data up to
32 levels deep, when you fetch data at a location
in the database, you retrieve all of
its children nodes. So if you have a
deeply nested tree, this might slow down the user. I also have to wait to get and
parse through all of this data. On top of that,
granting read and write access at a node grants
access to all the data under that node, which might
not actually be what you want. So in practice, keeping
this data structure as flat as possible will
probably be helpful. Now that we know how data
is stored in the Realtime Database, we're ready to
take a look at some code. Here, I'm in my
editor and starting from the same
starter code you'll have at the end
of our setup video linked in the description. Following that video involves
installing Firebase with NPM, which means you'll have Realtime
Database ready to use as well. So now in our entry
point file index.js, we can initialize a
Firebase app after grabbing the configuration information
from our Firebase project in the Console. Next, we'll have
to get the instance of Realtime Database, which is
used to read and write data. Every Firebase subpackage
has a getter function that retrieves an
instance of a service, like Realtime
Database in this case. As a side note, don't
worry about calling this getDatabase
function multiple times, as it returns the same
instance when you call it with the Firebase app. Now let's see how we
can write and read data from Realtime Database. We'll first have to
create a reference to a path, which is
where in the database we'll read and write data. And we'll use the ref()
function to do this. The first argument is
the database instance that we already got from
above, and the second argument is the path at which we want
to interact with the data. For example, let's say we're
creating a social exercise tracking app, and for it, we'll
probably have to add users. We'll call the ref() with
db, then pass in the path 'users/' + whatever
the user's ID is. To write to the database, we'll
use set() to save data to that reference. And if there's already
data existing at that path, the former data
will get replaced. Using the same reference
example above for adding users to our social
exercise tracking app, we'll call set() with a
reference to add a user to the database. Once we call the
writeUserData() function, we can see here in Firebase
Console that there is now this new entry. If we call that
function again with all of the same parameters,
except for a different email, the original email address
for the given user ID would be replaced
with the new email, as we can see here in
the Console, as well. Now that we know how to write
data, how do we get that data? Let's say we want to get the
total distance a user has run. We'll first get the
reference to the data, then use onValue
to observe events. onValue will get triggered once
when the listener is attached, and again every time
the data, including children data, changes. The listener receives
a snapshot that contains the data at the
specified path in the database at the time of the event. And we can get the value in the
snapshot with the val() method. This onValue() event is
helpful for syncing the entire app's state, since it
returns all of the data at the reference. Often in our apps,
we need to use lists. And a question
that comes to mind is, what happens if I
have a five-item list, and for whatever reason,
two devices randomly both decide to add a sixth
item at the same time? Well, we can use
Firebase's push() function, which generates a unique key
every time a child is added to the specified
Firebase reference. This way, we're
not trying to add an item at a particular
index in the list, which may result in an override
if device A adds to index 6, then device B does
the same thing. By using these
autogenerated unique keys for each new
element in the list, several clients can add
children to the same location at the same time
without write conflicts. Each key generated by push()
is based on a timestamp, so list items are automatically
ordered chronologically. For example, push() could
be used to add a new post to a list of comments
to workouts in our app. Now how do I get that
list of comments? Before, we had talked
about how to get data at a specific reference,
but that data wasn't a list. We can again use onValue(). But instead of using the val()
function on the snapshot, we'll iterate
through the snapshot, which returns the
entire list of data. For each item in the snapshot,
we'll get the corresponding value using the val() function. Sometimes, though, we don't
want to process the entire list. And, in fact, we
probably don't want to do that most of
the time because it's getting a lot of data. We likely care more about the
changes happening in that list, so let's listen to
child events instead. These are triggered in response
to specific operations that happen to the children of
a node from an operation, such as when a new child
node is added to the path. There are some events we can
listen for, and each of these together can be
useful for listening to changes to a specific
node in a database. For example, in our social
exercise tracking app, we might use these
methods together to monitor activity in
the comments of a workout. onChildAdded() retrieves
a list of items or listens to additions to a list of items. And this event is triggered
once for each existing child, and then again every
time a new child is added to the specified path. The listener is
passed a snapshot with the new child's data. So in this case, we'll get any
added comments for our post. onChildChanged() listens
for changes to the items in the list, and this event is
triggered any time a child node is modified, including any
modifications to descendants of the child node. The snapshot passed
to the event listener contains the updated
data for the child. In our example, this will
get triggered whenever a comment is
modified, and we'll be able to see the modified text. onChildRemoved() listens
for items being removed from a list, and this event is
triggered when an immediate child is removed. The snapshot passed
to the event listener contains data for
our removed child. So in our example,
this will get called when a comment gets
deleted, and we'll be able to see what was deleted. To make all of
this much simpler, there are open-source libraries
for popular web frameworks to use Realtime
Database more easily, AngularFire,
ReactFire, and VueFire. So that's it. We can use Realtime Database
to store and sync data as JSON, and this data is synced across
all clients in real time and remains available when
your app goes offline. The Realtime Database JavaScript
SDK is open source on GitHub, so check that out. And there are more "Firebase
Fundamentals" videos if you enjoyed this one. Happy coding.