Firebase Bootcamp: Hex-Clicker
Tags: firebase, javascript, gamedev, bootcamp
Published: 2022-10-15 11:40:40

## What is Firebase ?

Firebase is a Google-backed application development software that enables developers to develop iOS, Android and Web apps.

Firebase provides tools and serviced such as:
- Analytics
- Authentication
- Cloud functions
- Realtime database
- Crashlytics
- Performance monitoring, and much more...!

and all this serverless.

## Why use Firebase ?

- It is less technical and time-saving alternative to writing full-fledged backend code for dynamic apps.
- You might also want to consider leveraging this tool if you eventually wish to host and manage your app in the cloud.
- Being serverless, Firebase removes the need to worry about the technicalities of cloud server configuration.
- It gives easy access to connect and use built-in third-party authentication providers, including Google, Facebook, Twitter, among others.
- Low cost as firebase pricing is flexible and has pay-as-you-go services as well.
- Firebase is a good choice if you want to deploy a working product on the cloud rapidly.

## Hands on demo:

Let's make an easy and simple clicker game using HTML, CSS & JavaScript and see how we can use Firebase can make it more amazing.

### Flow of the demo:

1. Initiate a Firebase project.
2. Create a basic clicker game and host it on Firebase.
3. Add google login to the web-app.
4. Use Firestore to store user details and total clicks.
5. Set-up Firestore database rules.
6. Create a leaderboard for the game.

## Pre-requisites:
1. Install [VSCODE](https://code.visualstudio.com/download) (or any other code editor of your choice) on your machine.
2. Download [NodeJS](https://nodejs.org/en/download/) thus have NPM.

So let's get started 🥳

## 1. Initiate a Firebase project:

You can start a project direct from [Firebase site](https://console.firebase.google.com) or directly from your terminal.
1.1 Click on **Create a project** to start a new project.
![Create project via firebase site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ji4curpxu2pb89612o11.png)

1.2 To initialize your project from your VSCODE Terminal:

1.2.1 Install **Firebase SDK** and **Firebase Tools**

```npm install firebase```
```npm install -g firebase-tools```
![Installed Firebase SDK and tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4i653g9vcuhlxdf943t.png)

1.2.2 Login

```firebase login```
![Firebase Login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rn9tzptddjj0nd3ki73c.png)

1.2.3 Initialize a project

```firebase init```
![Initiated firebase project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvntoetzlefjda1fd8n0.png)

You will see on the left side file explorer that some files have been made.

![File explorer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gb4xigek863a9rqm5i2.png)

Apart from the **public/index.html** all other files/folders are of no use to us. They are for configurations and we can ignore them

Run **firebase emulators:start** to see the project running on your localhost.

![Local host](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnpn11e4wacng0o1dcae.png)
![Local host Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4y9d73lxll4c2dfsrksy.png)

You will see that the **index.html** given to us as boilerplate include many unnecessary things, so let's edit those out and create a simpler version for our selves.

### index.html
```html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Hex Clicker</title>

  <!-- update the version number as needed -->
  <script defer src="/__/firebase/9.12.1/firebase-app-compat.js"></script>
  <!-- include only the Firebase features as you need -->
  <script defer src="/__/firebase/9.12.1/firebase-auth-compat.js"></script>
  <script defer src="/__/firebase/9.12.1/firebase-firestore-compat.js"></script>
  <!-- 
      initialize the SDK after all desired features are loaded, set useEmulator to false
      to avoid connecting the SDK to running emulators.
    -->
  <script defer src="/__/firebase/init.js?useEmulator=true"></script>

  <!-- script and style file made by us -->
  <script src="./script.js"></script>
  <link rel="stylesheet" href="./style.css">

</head>

<body>
  <p>Hi there</p>
</body>

</html>
```
To simplify the code more, let's make separate **CSS** and **JavaScript**file.

![File explorer view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4u5bck98mw7l8lq3m1n.png)

### script.js
```javascript
window.onload = () => {
    console.log("Firebase loaded")
}
```
You can refresh the localhost and see the changes made, open dev tools press **CTRL + SHIFT + I** to open it. You can see that fire base has been loaded.

![page ss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jexi9bozmukqtrqwp36.png)

## 2. Create a basic clicker game and host it on Firebase.

Let's create a button and attach a **JS** function to increase the count on click.

### index.html
```html
...
<body>
  <button id="count-btn">
    <span id="count-value">0</span>
  </button>
  <p>Click to increase count !</p>
</body>
...
```

### style.css
```css
#count-btn{
    font-size: large;
    padding: 10px 40px;
    cursor: pointer;
}
```

### script.js
```js
let countScore = 0
window.onload = () => {
    console.log("Firebase loaded")

    document.getElementById("count-btn").onclick = () => {
        increaseCount()
    }
}

const increaseCount = () => {
    countScore++
    document.getElementById("count-value").innerText = countScore
}
```

Our basic clicker game is ready!!! 🎉
Let's host it so that you can send it to your friends so that they can play as well

Run **firebase deploy**

![Firebase deploy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akcwkmoazdau1dmza4t9.png)

![Site online](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cvv4psye14a0xci87anm.png)

🥳🥳🥳and.... it is online !


### 3. Add google login to the web-app.

Before we move forward and add login, let's enable Authentication and add permissions so that we can test features on our local host.

Move to your project on firebase console.
Goto Authentication > Sign-in method
Choose **Google**
![sign in method](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ditnz94cwvrguzvg465.png)

Goto Settings > Authorized domains
Add **127.0.0.1**
![Authorized domains](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et6ajr31x486ruc1azry.png)

Now we are ready to add sign-in button and functions in our app.

### index.html
```html
...
<body>
  <p id="user-details">your details...</p>
  <button id="sign-in">sign in</button>
  <button id="sign-out">sign out</button>
  <br><br>

  <button id="count-btn">
    <span id="count-value">0</span>
  </button>
  <p>Click to increase count !</p>
</body>
...
```

### script.js
```javascript
let userDetails = undefined
let countScore = 0
window.onload = () => {
    console.log("Firebase loaded")
    document.getElementById("count-btn").onclick = () => {
        increaseCount()
    }
    document.getElementById("sign-in").onclick = () => {
        googleSignIn()
    }
    document.getElementById("sign-out").onclick = () => {
        signOut()
    }
    firebase.auth().onAuthStateChanged((user) => {
        if (!user) {
            userDetails = undefined
            console.log("user not signed in")
            return
        }
        userDetails = {
            "name": user["multiFactor"]["user"]["displayName"],
            "email": user["multiFactor"]["user"]["email"],
        }
        console.log(userDetails)
        document.getElementById("user-details").innerText = `Hi ${userDetails["name"]} 👋`
    })
}

const increaseCount = () => {
    countScore++
    document.getElementById("count-value").innerText = countScore
}

const googleSignIn = () => {
    let provider = new firebase.auth.GoogleAuthProvider();
    firebase.auth()
        .signInWithPopup(provider)
        .then((result) => {
            console.log("sign in successful")
        }).catch((error) = () => {
            console.log(error);
            console.log("sign in successful")
        })
}

const signOut = () => {
    firebase.auth().signOut().then(() => {
        console.log('Sign-out successful')
    }, (error) => {
        console.log('Sign-out failed')
        console.log(error)
    })
    userDetails = undefined
}
```

![After login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8kzutzncvtr2rql13dj.png)

You can see all the user logins:
![All users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u8wp3q4f89regdfa55rq.png)

### 4. Use Firestore to store user details and total clicks.

Now that we have Google login added, let's store user details and total-clicks in firestore database.

Move to **Cloud firestore** > **Create database**
create in test mode > choose default location
![create database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9abycpx2tncj6dkaf3dk.png)

Start **game** collection and add the following dummy data:
![dummy data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ypzyb3408x6s1rwatj4f.png)

Now that we have a database set up, let's do the following:
- Add user data on sign-up
- increase clicks count on each click
- showcase total clicks

Get user data function:
```javascript
const getYourData = async () => {
    let docPath = `/game/${userDetails["email"]}`
    let dbData = await firebase.firestore().doc(docPath).get()
    return dbData.data()
}
```

Create new user data function:
```javascript
const createNewDoc = () => {
    firebase.firestore().collection("game").doc(userDetails["email"]).set({
        "name": userDetails["name"],
        "clicks": 0,
        "email": userDetails["email"]
    }).then(() => {
        console.log("new data added")
    });
}
```

Update user data function:
```javascript
const updateData = () => {
    firebase.firestore().collection("game").doc(userDetails["email"]).update({
        "clicks": firebase.firestore.FieldValue.increment(1)
    }).then(() => {
        console.log("data updated!")
    })
}
```

Increment function
```javascript
const increaseCount = () => {
    countScore++
    document.getElementById("count-value").innerText = countScore

    if (userDetails == undefined) return
    updateData()

    totalClicks++
    document.getElementById("clicks").innerText = `Total clicks: ${totalClicks}`
}
```

When user sign-up (sign-in for the first time), we will check in the database if doc for the user already exists if it doesn't we will create new data.
And if users clicks on the button update the value.

```javascript
...
firebase.auth().onAuthStateChanged(async (user) => {
        if (!user) {
            userDetails = undefined
            console.log("user not signed in")
            return
        }
        userDetails = {
            "name": user["multiFactor"]["user"]["displayName"],
            "email": user["multiFactor"]["user"]["email"]
        }
        console.log(userDetails)
        document.getElementById("user-details").innerText = `Hi ${userDetails["name"]} 👋`

        // retrieve user data if it is undefined then create new doc
        let dbData = await getYourData()
        if (dbData == undefined) {
            createNewDoc()
        }
        dbData = await getYourData()
        totalClicks = dbData["clicks"]
        document.getElementById("clicks").innerText = `Total clicks: ${totalClicks}`
    })
...
```
![Total clicks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlgqrti186snvq38bi9k.png)

![DB ss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmameyy3fgr0e76sll6v.png)

### 5. Set-up Firestore database rules.

Currently our database is open, that means anyone can edit anyone's data, this is where we can use firestore rules.
We will add rules so that everyone can read the database but only only signed in users can update database docs and even then they can only update their own docs.

```firestore
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /game/{document} {
      allow read,create: if true;
      allow update: if request.auth != null && document == request.auth.token.email;
    }
  }
}
```
![DB rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g5f3wgfahduvw0hwoao.png)

Our demo project is almost done..!

### 6. Create a leaderboard for the game.

Let's create leaderboard so that others can also see their friends progress.

Get data of every user:
```javascript
const getAllData = async () => {
    let docPath = "/game"
    snapshot = await firebase.firestore().collection(docPath).get()
    return snapshot.docs.map(doc => doc.data())
}
```

### index.html
```html
...
<button id="get-board">Get leaderboard</button>
  <div class="leaderboard">
    <div class="block">
      <div class="name">Username</div>
      <div class="score">Total clicks</div>
    </div>
    <div id="leaderboard-data">

    </div>
  </div>
...
```

### style.css
```css
#count-btn{
    font-size: large;
    padding: 10px 40px;
    cursor: pointer;
}

.leaderboard{
    display: flex;
    flex-direction: column;
}

.block{
    display: flex;
    flex-direction: row;
}
.block div{
    padding: 4px 10px;
}
```

### script.js
```javascript
...
document.getElementById("get-board").onclick = async () => {
        let allData = await getAllData()

        let divData = ""
        for (let i = 0; i < allData.length; ++i) {
            divData += `<div class="block">
            <div class="name">${allData[i]["name"]}</div>
            <div class="score">${allData[i]["clicks"]}</div>
          </div>`
        }
        document.getElementById("leaderboard-data").innerHTML = divData
    }
...
```

![Leaderboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo8iday3jpqnq4i3bo09.png)

And with this we are done 🥳🥳🥳
Now you can style it more as you wish make it more interesting add more feature... endless possibilities.

## Conclusion

I hope this article gave you an insight on Firebase on when, where and why you should use it. The demo project is made in a way that it keeps the bare minimum code and style so that it is easier for you to refer if you get stuck.

Hosting, Authetication and Database are the bare bones that are needed for any web-app project. So make more intersting web-apps with firebase and if you do make them post abou it on Linkedin and do tag me as well. 

Happy learning ...🎉

## Project links

- Demo site link: https://hexclicker.web.app
- Final site link: https://hex-clicker-28.web.app
- Github repo: https://github.com/jhamadhav/hex-clicker

## My socials

- Linkedin: [@jhamadhav](https://www.linkedin.com/in/jhamadhav)
- Twitter: [@jhamadhav28](https://twitter.com/jhamadhav28)
- Github: [@jhamadhav](https://github.com/jhamadhav) 
- Website: [jhamadhav.com](https://jhamadhav.com/)