Firebase Database

Last week we created users for our app. This week we will start connecting the users to our database.

Firebase Database

Next we'll make a function but first we have to set up the database in the Firebase Console.

Firebase has a new database called Cloud Store, but we want to work with the Realtime Database for our app, so scroll down and choose that one.

In the Database tab, click Create Database.

We can practice adding data manually here. Firebase is awesome because we can see how the data is structured and watch it update in real time.

Rules

Let's also take a quick look at the Rules tab. This determines which users can access to read or write to the database.

The default rules look like this.

{ "rules": { ".read": true, ".write": true } }

This basically means anyone can access any part of the database. That's not a good rule set. Let's update the ".write" rule.

{ "rules": { ".read": true, ".write": "auth != null" } }

This is a really basic rule that says if you are not authenticated you cannot write anything to the database, but you can still read content from the database.

Later when we add posting, we'll restrict these rules further based on different parts of the database.

Here is a list of some good default Firebase rules.

Add a user to the database

We're going to update the updateUser function in auth.js to add the new user to the database.

To do this we will use credential.user.uid, the unique user ID generated by the Firebase Authentication table, as the user key.

First, for this to work, we need to add the firebase-database.js module in our HTML file.

<script src="https://www.gstatic.com/firebasejs/5.9.1/firebase-app.js"></script> <script src="https://www.gstatic.com/firebasejs/5.9.1/firebase-auth.js"></script> <script src="https://www.gstatic.com/firebasejs/5.9.1/firebase-database.js"></script>
createButton.onclick = function() { const promise = firebase.auth().createUserWithEmailAndPassword(emailInput.value, passwordInput.value); promise.catch(function(error) { errorMessage.textContent = error.message; }); promise.then(function(response) { createUser(response.user); }); }; function createUser(user) { const db = firebase.database(); const ref = db.ref('users').child(user.uid); const promise = ref.update({ userName: userInput.value }); promise.then(function() { location.href = 'index.html'; }); }

Now that we have users, let's update our database rules to restrict users from writing to the users part of the database unless their login uid matches the page uid.

{ "rules": { "users": { "$uid": { ".read": true, ".write": "$uid === auth.uid" } }, ".read": true, ".write": false } }

Here the "$uid" represents whichever user's profile is loaded, matching the reference inside the "users" part of the database.

Display Data

Now that we have stored a user in the database, we want to display the user information on their profile page.

We'll start by adding a link to the profile page.

Then update the authState function in auth.js to add a link using the uid.

By adding the user id as a search term, with the ? separator in the URL, we can retrieve the user info from the database by referencing the uid.

const displayName = document.getElementById("user-name"); const profileButton = document.getElementById("profile-button"); firebase.auth().onAuthStateChanged(function(user) { if (user) { document.body.classList.add('auth'); const userRef = firebase.database().ref('users').child(user.uid); userRef.once('value', function(snapshot) { const userInfo = snapshot.val(); displayName.textContent = "Welcome, " + userInfo.displayName; profileButton.onclick = function() { location.href = "profile.html"; }; }); } else { document.body.classList.remove('auth'); displayName.textContent = ""; } });

To create a profile page, let's make a new HTML file called user.html. This page will show users profiles to be viewed by other users, or edited if the user views their own profile.

This page will be almost identical to index.html except we will add a new script, js/profile.js and a new HTML section for the profile information.

<div id="main"> <input id="profile-name" placeholder="User name"> <textarea id="bio" placeholder="Write your bio."></textarea> <button id="update-profile">Update Profile</button> </div>

Our script will get the uid from the profile link, which looks something like this.

http://localhost/user.html?uid=Ge8r8xjMCUZMhRUEIPf8xdxOcMy1

That uid is used to reference the user's information from the database.

const profileName = document.getElementById('profile-name'); const bioInput = document.getElementById('bio'); const updateButton = document.getElementById('update-profile'); const uid = location.search.split('=')[1]; const userRef = firebase.database().ref('users').child(uid); userRef.on('value', function(snapshot) { const userInfo = snapshot.val(); profileName.value = userInfo.userName; if (userInfo.bio) { bioInput.value = userInfo.bio; } });

ref.on('value') is an event that triggers when the database detects data has been added or changed at this location. Test this by changing the data manually in Firebase.

The data should update without reloading the page. This is the realtime aspect of Firebase

ref.once('value') can be used if the data should not be updated in realtime.

Firebase refers to the data returned by events as snapshots. That's why the argument of updateUser is snapshot.

snapshot.val() is used to get the actual data from the event. The snapshot itself has more properties that can be used to add other events and responses.

Updating data

Starting with the profile, our user should be able to edit their information after it has been added to the database.

We'll start with the username.

The user will only be able to edit this information if they are viewing their own profile page.

updateButton.onclick = function() { userRef.update({ userName: profileName.value, bio: bioInput.value }); };

The edit button should only be visible if the user is logged in, so we can add to the CSS where we display content based on the .logged-in in class.

The #update-profile section will not be displayed until the edit button is clicked.

#edit, #profile-edit { display: none; } .logged-in #edit { display: block; }