--- tags: mstu5013, tutorial, js, firebase, realtime --- # Firebase RealtimeDB Tutorial 02: Reading and Writing Data ## Quick Refresh As you may remember from our first [Firebase Tutorial](https://hackmd.io/qvmPiuLmRH-FYW8476pWfQ#Firebase-Tutorial-01-Introduction-and-Basics), data in Firebase is structured very much like a Javascript Object. And we can point to different depths within that object via a reference that looks very much like a URL. Take the following example as a reminder: **Model Data in Firebase:** ```javascript "users": { "Woody": { "toy": "cowboy" }, "Buzz": { "toy": "space ranger" } } ``` If we want to change the Woody's `toy` property value from `"cowboy"` to `"sherrif"` the reference I'm interested in updating would be: ``` /users/Woody/toy ``` If I wanted to remove Buzz Lightyear from my database the reference I'm interested in removing is: ``` /users/Buzz ``` If I wanted to add a new toy (Rex the dinosaur) I would set data, ``` /users/Rex ``` to the object `{ "toy": "dinosaur" }` References and understanding how they represent specific locations in the database hierarchy is a core concept from which most operations are derived. ## Caching and Building References You can always write out references in full. E.g. `/users/abc123/posts/Jxs342kfis3/message`. But some references tend to be more commonly used in our apps than others. As such, we can and should locally store or cache our references as variables. That way, instead of spelling out a reference every time, we can just use the variable instead. Furthermore we can shortcut the process of spelling out longer refs by building off of existing cached references. ```javascript= // I am user abc123 var userID = "abc123"; var database = firebase.database(); // Cache "/users" ref as usersRef var usersRef = database.ref('/users'); // Cache "/users/abc123" as myRef by building a reference string var myRef = database.ref('/users/' + userID); // Cache "/users/abc123/posts" as myPostsRef by building off of myRef var myPostsRef = myRef.child('posts'); ``` In the above example: - **Line 4:** the _foundation_ of all references `firebase.database()` returns a database object. - This object has the method `.ref()` which we use after to create references. - **Line 7:** `.ref()` method returns a reference Object that points to `/users` - The argument is a reference String - **Line 10:** shows how we can manipulate the reference String argument that we pass into the `.ref()` method - **Line 13:** shows how we can build references from other references Objects - `myRef` comes from Line 10 and already points to `/users/abc123` - `.child()` is a method of available to all reference Objects and is used to construct deeper references. Hence, `myRef.child('posts')` - `.child('posts')` builds off of `myRef` to result in `/users/abc123/posts` and we cache this as `myPostsRef` Hence, we can dynamically build references and store them in variables. Whether reading or writing data, we often use the same references again and again. So it makes sense to be able to cache and build these. In the next sections, we'll examine specific methods of the reference object that allow for the basic operations: WRITE, READ, UPDATE, DELETE. :::info **NOTE:** Don't forget, you'll need to change your firebase rules from `null` to `true`, otherwise you wouldn't be able to apply changes to your database. ```json { "rules": { ".read": true, ".write": true } } ``` ::: ## Writing Data So far, we have been using variables as a way to store our data. Local variables provide temporary storage, and everytime the page is reloaded the data is lost. Databases, however, allow us to store data that will persist even if the user closes the navigation window and opens it at a later time. By writing data to the database, we are storing data that will persist even if the state of the program changes. Such data can be fetched later (READ) to push the state of the program back to where it was at the last close. To write data there are basically two conceptual things you need to do: 1. Define a reference to write to: - Can be a new reference point - Can be an existing reference point 2. Write the data at that reference point. ### `.set(data)` Method All reference objects have a `.set()` method available to write at the defined reference. The pattern is: ``` refObject.set(data); ``` **Example:** ```javascript var db = firebase.database(); // catRef is a reference Object pointing to "/animals/cat" var catRef = db.ref('/animals/cat'); // Writes the following data at /animals/cat catRef.set({ name: "Fluffy", sound: "meow" }); ``` `.set()` will write data to the database by replacing all data at the reference point selected. Thus, in the above example it will replace _ALL_ data at `/animals/cat` reference with `{ name:"Fluffy", sound:"meow" }`. If I wanted to change the cat's name from `"Fluffy"` to `"Furball"` and I do the following we will have a problem: ```javascript catRef.set({ name: "Furball" // Problem... what happens to sound: "meow" ??? }); ``` Remember that `.set()` replaces all existing data at `/animals/cat` reference with the new data you pass in. The result is a _new_ cat object at `/animals/cat` that only has a name. The prior cat Object with `sound: "meow"` is gone. If we only wanted to change the name using `.set()` we could be more specific with the reference. ```javascript catRef.child('name').set("Furball"); ``` Here, I am using the `.child()` method to return a more specific reference Object that points to `/animals/cat/name`. I set the value of that reference to the String `"Furball"`. Other data is therefore not affected. #### Combining it with User Input Here's another example. Let's say that we want to save users' first and last names into the database. We could use the `.set()` method as follows: **Vanilla HTML/JS Example:** ```html= <input type='text' id='userFirst' value=''> <input type='text' id='userLast' value=''> <button type='button' onclick='saveUserData()'>Submit</button> <script> function saveUserData() { // Getting user data var userFirst = document.getElementById('userFirst').value; var userLast = document.getElementById('userLast').value; // Writing to the Database var database = firebase.database(); database.ref('users').set({ first: userFirst, last: userLast }); } </script> ``` This code will create a brand new node `users`, which will be set at the root of our database, and set `first` and `last` as its children. Our realtime database will look something like this: ![](https://i.imgur.com/ShgS1Q5.png) But what if there are going to be multiple (millions) of users? You might be thinking of a data structure like the following: ```json { "users": { "anna": { "first": "Anabel", "last": "Bugallo" }, "jin": { "first": "Jin", "last": "Kuwata" } } } ``` And that would be a good architecture. All specific users should be found under the reference `/users`. This way, if we want to grab a list of users we can simply read 1 reference at `/users` and be done with it. To add a third user I could do this: ```javascript= var usersRef = firebase.database().ref('/users'); usersRef.child("john").set({ first: "John", last: "Wick" }); ``` But we have a problem in terms of writing in this way. Up until now, our examples used reference keys like `"anna"` and `"jin"` which were convenient in terms of conceptualization. But, when we think about data architecture, we need to think about the _uniqueness_ of references. These references that identify _unique_ data are also known as _keys_. - `"anna"` is the key (and ref) that points to her data object - `"jin"` is the key (and ref) that points to his data object - `"john"` could be the key that points to his data object So we set a new user at the reference `/users/john` and gave it some data. But how many users might have the name "John" in the world? Probably quite a few. The value of keys are in their uniqueness. Custom keys that are _easy on the eyes_ have their place. For example, if this was a database of **baby names** where each first name would only have 1 entry - keys like `anna`, `jin`, `john` work fine. If I were making a dictionary of sports hobbies, keys like `soccer`, `baseball`, and `hockey` work because I can guarantee uniqueness. But it's a bad idea to use first names as keys for users in a large app. And it's a pain in the but to come up with a system that guarantees a unique key ourselves. > Fortunately, firebase has a way to produce unique keys. ### `.push(data)` Method Up until now, we used the `myRef.set()` method to write data to a specific and defined reference. Although we can manually create unique identifiers, like we demonstrated prior, it will be time consuming and exhausting. Fortunately, reference Objects have a `.push()` method that generates unique keys for each new child added. **Conceptual Example** ```javascript refObj.push(data); ``` The significance of `.push()` is that `refObj.push(data)` will create a unique key as a child to the `refObj` node, and set the data at that key reference. **Comparison `.set()` vs. `.push()`** ```javascript var animalsRef = firebase.database().ref('/animals'); ``` **`.set()`** ```javascript animalsRef.set({ animalType: "tiger" }); // Sets data to: - animals - animalType: "tiger" ``` Here, data is simply set directly to the reference. **`.push()`** ```javascript animalsRef.push({ animalType: "tiger" }); // Sets data to: - animals - Lx39slif03xm02dkv - animalType: "tiger" ``` Here, a unique key is pushed at the reference. Data is set at this new unique key reference. The main functionality of `.push()` is actually to generate a reference Object and a unique key. You can pass data into it or not. In the above example, we passed data as an argument into the `.push()` method. If data is passed in as an arg, then `.push()` method will first create a new reference at some unique key, then execute a `.set()` to write to the database. If you don't pass in any data immediately into `.push()` you get a reference Object back but do not immediately `.set()`. This can be rather useful. For example: ```javascript= // Reference to Firebase Database instance var database = firebase.database(); // Generating and storing a unique key per user var myKey = database.ref('users').push().key; // E.g. -L781LH2B_K_7LTmhGsW // users/-L781LH2B_K_7LTmhGsW database.ref('users/' + myKey).set({ first: userFirst, last: userLast, // id: myKey }); ``` - **Line 5:** `database.ref('users').push()` generates a new key e.g. `Lx930sb903m` and returns a reference Object at that key. - At this point, nothing is saved on the database since we did not pass any data arguments into the `.push()` method. - However, the returned reference Object is has a property called `key` that we can access to save the unique key. - **Line 8:** Notice how we are adding `myKey` to the reference path. In this way, instead of setting `first` and `last` as direct children of `users`, we are setting them to the unique key identifier `myKey`. By doing this, we don't have to worry about overwriting our data because we are not writing directly to the root `users`. Instead, we are generating unique alphanumeric values based on a timestamp, which will result in unique key identifiers. - **Line 11:** Also notice here, that I could include the key as a data within the user object itself. This is a handy technique that'll come into use later but is not reflected in the examples below. Let's take a look at how a unique key looks like: ![](https://i.imgur.com/CAFlqwm.png) By using unique keys, we can easily have millions of users in our database without worrying about overwriting our data. For every new user added, we will have a new node represented by a unique key, containing the info pertaining to that user. Often times we create complex data objects with a variety of properties first. Then `.push()` for a new unique key reference, then `.set()` the data at that reference. ```html= <h2>Enter your message to the world:</h2> <input type="text" id='author' value=""> <textarea type="text" id='msg' value=""></textarea> <button type="button" onclick="saveMsg()">SEND</button> <script> function saveMsg(){ var myMessage = { author: document.getElementById('author').value, msg: document.getElementById('msg').value }; // Firebase Database instance var database = firebase.database(); // Generating a unique key identifier var myKey = msgRef.push().key; // Writing to the database database.ref('messages/' + myKey).set(myMessage); // ALTERNATIVE // database.ref('messages').child(myKey).set(myMessage); } </script> ``` In this case, we are using the `set()` method to save the `myMessage` object into the database. Because we are using unique keys, we have no risk of overwriting our data. This is how our data structure looks like: ![](https://i.imgur.com/E2TE8Ff.png) --- ## Reading Data Remember that with realtime databases, we aren't just requesting data at discrete moments of the app usage. E.g. fetch data when the page loads. We're interested in live changes so we need to continually monitor the data on the database. To read data we will be using listeners that will detect any change produced at the specified location in our database. Therefore, we need to specify the reference point from where we want to read our data. To read data we need to utilize three concepts: - An **event listener:** we will be using the `on()` and `once()` method to listen for changes. - An **event trigger:** we will be listening for triggering of specific events that indicate changes in data. - A **callback function:** to execute some code when an event is triggered. - This callback will provide us a `snapshot` object (similar to the concept of an `event` object) that contains our data at the specified location. ### Listening for changes in database To read data, we are going to use event listeners. We can choose if we want to read data at the specified location once or everytime there's a change. #### `.on()` and `.once()` Methods These functions are also methods of firebase reference objects. The pattern therefore is: ``` refObj.on(event, callback); ``` OR ``` refObj.once(event, callback); ``` In implementation, the pattern looks more like this: ```javascript= myRef.on('eventName', function(){ // handler code to be executed }); ``` > Every time the `eventName` is triggered, the function will be executed. **Example with annotation:** ```javascript= // PATTERN: // ref.on('event', callback); // Reference point where we want to listen from var myRef = firebase.database().ref('path'); //Everytime a value event is fired, run the callback function myRef.on('value', function(snapshot){ // handler code to be executed }); ``` Data requires active listening when it's constantly changing and we want to be notified any time the data has changed. Once we are "notified" via the event trigger, and our listener picks this up, our callback can execute code that will update our page. For example, if we think of a live Twitter feed. Every time someone creates a new tweet, it gets posted automatically in the feeds section. In cases like this, it's convenient to have an event listener that would listen for changes produced at the *tweets* node, and notify the client app of the new *tweet*, so that it can be rendered on their page. If, on the contrary, we want to listen for changes once, and then stop listening, we can use the `.once()` method as in below: ```javascript= // Reference point where we want to listen from var myRef = firebase.database().ref('path'); myRef.once('value', function(snapshot){ // handler code to be executed }); ``` In this case, the callback function will be triggered just once, and it won't be triggered again. We want to use this method when data needs to be loaded only once, and we don't expect it to change very often. For example, maybe you want to load one _blog post_. You're probably not expecting a blog post to change often while a user is reading it. Nor is it that important that a change is updated live. In this case, `.once()` will suffice. On the other hand, if it's a collaborative blog post editor, where you want multiple people able to write at the same time - then it's very critical to make sure every change is listened to and handled. `.on()` is the way to go here. If you want to stop listening for data changes, you can use the `.off()` method. Using `.once()` is equivalent to using the `.on()` and executing `.off()` immediately after the listener is triggered and the callback is handled. #### `"value"` Event As we saw before, `value` is the first argument (a String) and name of the event we're using in the `.on()` and `.once()` methods. A `value` event listens for any changes in the _value_ at the particular reference you are listening to. - If something is added at that reference or deep within, the event is triggered. - If something is changed at that reference or deep within, the event is triggered. - If something is removed at that reference or deep within, the event is triggered. Basically, any data change triggers the `value` event. This is the workhorse we will use for most of our operations. However, you can read more about other events (i.e. `child_added`, `child_removed`, `child_updated` etc.) [here](https://firebase.google.com/docs/reference/js/firebase.database.Reference#on). Our event listeners are waiting for `value` events to happen at the specified reference to trigger the callback function where we will: 1. Get access to the data 2. Do something (e.g. update our app) with that data #### Callback Function The second argument that the `.on()` and `.once()` methods is a callback function. The occurence of any `value` event will trigger the callback function. This function will return an object with an updated version of the data at the specified location as well as other information. And that is what we call a `snapshot`. The `snapshot` is the argument that we are passing into our function. Therefore, the listener will receive a snapshot of the data everytime following the occurence of the event. Compare this to how we used `event` when dealing with mouse clicks. **MouseClick Event** ```javascript var buttonEl = document.querySelector('button'); buttonEl.on('click', function(event) { // event, e, banana - don't matter console.log(event); // analogous to snapshot console.log(event.type); // tells us a bit about the event console.log(event.target); // provides information about the event element }); ``` **Firebase Value Event** ```javascript var postsRef = firebase.database().ref('/posts'); postsRef.on('value', function(snapshot) { // snapshot, snap, banana - don't matter console.log( snapshot.val() ); // The actual data at the reference point console.log( snapshot.key ); // Key, last part of path of location of snapshot console.log( snapshot.ref ); // The reference for location of snapshot // CODE to do something with data goes here: e.g. update Riot with new data }); ``` The most valuable part of the `snapshot` is the actual data within it. We can access it by using the `.val()` method which all snapshot objects have. `snapshot.val()` returns the raw data from the `snapshot` object. Let's see a snippet of code: ```javascript= var usersRef = firebase.database().ref('users'); usersRef.once('value', function(snapshot){ var data = snapshot.val(); // temporarily store the raw data console.log(data); }); ``` Snapshot of data printed on the console: ![](https://i.imgur.com/pqae5S3.png) As you can see, `snapshot.val()` returns an object. This particular object in our example has many properties (i.e. the unique keys) which are each set to a particular user object with `first` and `last` names. If there's no data at a reference at the time of the event, the snapshot will return `null`. Let's see an example working out both write and read operations: **DATABASE** ```json { "users": { "-L0d9f73ngs": { "first": "Mickey", "last": "Mouse" }, "-L34sif9dfn": { "first": "Minnie", "last": "Mouse" } } } ``` **CODE** ```htmlmixed= <ul id="userList"> <!-- users <li> will be rendered here --> </ul> <input id="userFirst"> <input id="userLast"> <button onclick="saveUser()">Add User</button> <script> var database = firebase.database(); var userRef = database.ref('users'); // ref that has each user as a child function saveUser(){ // Getting user input var user = { first: document.getElementById('userFirst').value, last: document.getElementById('userLast').value }; // Writing to the Database var myKey = userRef.push().key; userRef.child(myKey).set(user); } // Reading from the database userRef.on('value', function (snapshot) { var users = snapshot.val(); var listHTML = ""; for (key in getUsers){ listHTML += `<li>${ users[key].first } ${ users[key].last }</li>`; } var listEl = document.getElementById('userList'); listEl.innerHTML = list; }); </script> ``` - **Lines 1-8:** User interface - Place for users to be listed as list items - Inputs to create new users - Button to add/save user - **Lines 11-12:** Caching references - **Lines 14-24:** Adding/Saving user to database - Create user object with user input info - Push a unique key at `/users` e.g. `-L9dhjs90k4` - Set the data at that new reference - **Lines 27-38:** Listen for changes at `/users` - Listener code exists at the global level. - Listener is activated as soon as this page loads, starts listening for any value changes. - When we get the data (an object of unique users) from the `snapshot` via `.val()` and store it in the variable `users` - Note that the raw data is an object and not an array. - We use a `for ... in` loop to loop through the object and create HTML list items (string) for each user. - `for ... in` is a way to loop through an object and it's properties. - **Line 32:** key represents the object property, getUsers the larger object. Hence, each user is represented by `users[key]`. - The `<ul>` `.innerHTML` is set to the list items we created. ## RIOT and Read/Writing of Data When using RIOT, you'll find that there are not too many differences in the way we read and write from the database. We still need: - An instance of the firebase database. ```javascript var database = firebase.database(); ``` - A reference to the location where we want to read from and/or write to. ```javascript var myRef = database.ref('location'); ``` > The most important aspect of utilizing Firebase in Riot, is to think about where in our tag files, the firebase code should reside. ### Writing data When using RIOT, our code to write to the database is going to be placed in the `<script>` tag. It is best to place it in the parent tag since all the children can have access to it. ```jsx= <app> <label>Author:</label> <input type="text" ref="author"/> <label>Post:</label> <textarea type="text" ref="post"></textarea> <button onclick={savePost}>Save</button> <script> var that = this; // Firebase Database instance var database = firebase.database(); // Reference to messages var postRef = database.ref('posts'); that.savePost(){ var myPost = { author: this.refs.author.value, post: this.refs.post.value }; // Generating a unique key identifier var myKey = postRef.push().key; // Writing to the database using the child method postRef.child(myKey).set(myPost); } </script> </app> ``` In the above example, you can see Firebase related code sitting in two places. 1. Database and reference caching at the TAG level (accessible anywhere in this tag.) 2. Data setting within user interaction event handlers. Having the caching code at the tag level allows us to reuse those variables `database` and `postRef` in any other place within this tag. Since we might have multiple times we want to use the `postRef`, this is a good place to put it. More specific operations like writing to the database usually only happen after a user event like `click` to save. Thus, the code to `.push()` and `.set()` happen in our `this.savePost()` function. First we get the user inputs, we create a data object, we push a new key at `postRef`, and we set the user data at that ref. Now let's see how we can read the data that was saved in the previous example using RIOT. ### Reading data As you may remember, RIOT authomatically calls the`update()` method anytime a UI event is triggered. However, Firebase events (e.g. `value`) are not UI events. Riot is blind to the Firebase events and will not automatically update, even if our data does. Because of this, without accounting for it our component can get out of sync with the data. Thus, we need to create an explicit connection between the Firebase event handler, and our Riot Tag instance through some manual work. Let's see an example of how to hook up Firebase listeners to Riot: ```jsx= <app> // Creating a post tag per post item <post each={ myPosts }></post> <script> var that = this; this.myPosts = []; var database = firebase.database(); var postsRef = database.ref('posts'); postsRef.on('value', function(snap){ var data = snap.val(); var post = []; for (key in data){ post.push(data[key]); } that.posts = post; that.update(); // We need to manually update }); </script> </app> ``` As we did previously, our `database` and `postsRef` is cached at the TAG level as regular variables. These are accessible anywhere in our tag code. Our Firebase `value` event listener also exists at this level of the TAG. When a Riot tag is initialized, this code will execute on initialization. Meaning it starts listening as soon as the tag component is born. A common mistake is people place this listener code inside a Riot UI function like `this.getUsers()` that is only called when say, a button is pressed. So think about the relationship between where your Firebase listener code resides, and when it will execute and start listening. Within our Firebase listener code, we get the data through the `snap`, then loop through that raw object data to convert it to an array. The `for...in` goes through each property of the users data object, and pushes each user into an array structure. At this point, everything is happening in the Firebase listener callback. The state of the Riot `this.myPosts` is still an empty `[]` until **line 21**, where we set `that.posts` to the `post` array generated through our loop. Notice that we are referring to the tag as `that` (see also **line 6**). It's important because `this` is a JS keyword and inside the Firebase callback - `this` will not refer to the Riot tag. Using `that` declared at the TAG level gets around the limitation. So at **line 21**, we set the TAG array `that.myPosts` to the data we received and cleaned up from the `snap`. At this stage, the Riot state - the data IS now reflecting the newest data. But our component will still show the old data. Riot doesn't know to `update()` the component. Finally **line 22** executes. We manually call `.update()` on `that` tag via `that.update()`. Now the riot component will re-render itself based on the newest state data and be in sync. In this case we are using RIOT `each` method. If we `console.log()` the `<post>` tag we will see how RIOT creates a child tag per item in `posts`. ![](https://i.imgur.com/BOQLtkD.png) :::success **AUTHORS** By Anabel Bugallo and Jin Kuwata :::