Initialising a Firebase App
Tags: javascript, webdev, programming, lit
Published: 2022-03-20 21:50:37

## Introduction

Welcome to the latest update in the "Creating a Link-Sharing Website" series where I try to build a StackExchange and HackerNews crossover. Before this update, I had a couple of working UI elements and (hopefully) a cool idea. See my [last post](https://adamjhawley.com/post/2022-03-04-creating-a-link-sharing-site-part-1/) to see exactly how I got started building out the first core UI elements.

> If you want to jump straight to reading how I got started with Firebase, [click here](#initialising-a-firebase-project).

I concluded the last post by discussing how my top priority was to consider database solutions. This is because, at that point, a user could interact with the UI and *'submit'* links to the website but with no real effect. The UI would simply gobble up the form entries and do nothing with them. Content generation that doesn't involve generating content? Sounds pretty redundant to me.

---

## Deciding on a Storage Solution
So I needed a way to store the links users would be uploading to the website. Naturally, I started to consider a number of database solutions I have used in the past.

- [MongoDB](https://www.mongodb.com/): a document-based NoSQL database that I have extensive prior experience with.
- [Firebase](https://firebase.google.com/): actually a whole suite of services provided by Google. It includes, but is not limited to, two different database offerings, an authentication service and hosting.
- [ElasticSearch](https://www.elastic.co/elasticsearch/): in the past, I have worked with ElasticSearch as a database for storing text data. Because of this, I know that it has extremely powerful search and query capabilities.

When evaluating the different choices I had to think about what I was going to be storing. URLs and the title's of the pages. Text. The basis of the site is for people to share website links. If I want people to be able to share and view
them, I have to store them somewhere. Specifically, my plan is to store simple objects in the form:

```
{
  url: "https://adamjhawley.com/post/2022-02-07-using-git-rebase-to-perfect-commits/",
  title: "Using 'git rebase' to Perfect Commits"
}
```

Even though MongoDB would definitely be up to the job if I had opted to select it, I ultimately disregarded it. It was going to do exactly what it said on the tin; no more, no less. It would serve as a document-based database with developer-friendly APIs. So this would have definitely been my choice if another wasn't so tempting...

Despite the fact that I had experience using ElasticSearch to quickly navigate mountains of text data, I had an important realisation. I was not going to be doing any complicated queries. So what's the point? I had to stop myself from
dodging [Occam's razor](https://en.wikipedia.org/wiki/Occam%27s_razor) and consider something else.

It is important to me to keep the website true to itself. I know from experience that I, like many other developers, am easily distracted by the thought of adding cool new features, insights and analytics to websites long before they are necessary.

Now onto Firebase. As I mentioned earlier, Firebase is rather a whole suite of services provided by Google. Apart from a storage solution, Firebase offers [hosting](https://firebase.google.com/products/hosting), [authentication](https://firebase.google.com/products/auth) and several other services.

Initially it might appear as if I am going against what I just said with regards to keeping things as simple as possible. However, there is a key difference between the extra features of Elasticsearch and the other services offered by Firebase.

Solutions for hosting and authentication are things I would need to even get the base functionality of the site off the ground. For example, without a way to host the site, there is no way for users to reach it. Of course, I could handle
this myself but that would involve taking on a considerably large amount of work. Remember, at this point, [I am focusing on an MVP](https://adamjhawley.com/post/2022-03-04-creating-a-link-sharing-site-part-1/). Furthermore, I am considering making the website open source as a template for other developers to use. By using Firebase it means that it would be much easier for someone else to pick up the project. All they need is a Google account!

---

## Firebase

###  Initialising a Firebase Project
So how did I actually get started? The first thing I did was turn to the Firebase [web documentation on getting started](https://firebase.google.com/docs/web/setup). From there I followed the following steps:

1. Created a Firebase project using the [Firebase
   console](https://console.firebase.google.com/u/0/)
2. Installed the [Firebase CLI
   tools](https://firebase.google.com/docs/cli/#install-cli-mac-linux):
   `npm install -g firebase-tools`
3. Logged in using Firebase CLI: `firebase login`
4. Initialised the project as a Firebase app: `firebase init` with the following configuration:

![`firebase init` dialogue.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3uby2a7cfobyepzovdm6.png)

> 
Note: during the `firebase init` dialogue you might get an error similar to the following. Don't panic. For some reason, Google requires that users create the Firestore instance in the UI before it can be accessed with CLI tools. Simply follow the URL in the error and create a Firebase instance in your browser.
```{fig.cap="`firebase init` error"}
Error: It looks like you haven't used Cloud Firestore in this project before. Go
to https://console.firebase.google.com/project/<PROJECT-ID>/firestore to
create your Cloud Firestore database.
```

After this, Firebase CLI automatically generates a handful of config files which it will use for deploying to Firebase, configuring storage etc.

In order to submit links to Firestore (the Firebase storage service I have opted for), it requires a working authentication system. By default, Firestore does not allow for unrestricted access to the database. Besides, I plan to limit users to only be able to post links if they are signed in to an account. So authentication is a necessary step. So I decided it was best to start with it rather than working around it and then having to come back and rework in future.

---

### Problems with FirebaseUI

During the process of setting up the application to use Firebase authentication, I found *helpful* [documentation](https://firebase.google.com/docs/auth/web/firebaseui) on how to use a pre-built UI for authentication which apparently allows for:

>
* Multiple authentication providers (email/password, Google, Github etc.)
* Account linking
* Customisation
* One-tap sign-up and automatic sign-in
* Localised UI

This all sounded great. I tried to integrate this into the application but ran into an issue trying to import the package.

In the project, I use ES module imports. That is, imports in the format:
```{js es-module-imports}
import x from 'npm-module/app';
```
For some reason, this doesn't seem to be supported by the FirebaseUI. [This GitHub issue](https://github.com/firebase/firebaseui-web/issues/674) has been open since 2020 with no clear progress seemingly made.

After reading through the issue thread I decided to abandon using the FirebaseUI and build my own authentication UI.

---

### Creating an Sign-Up Page
![Link-Sharer sign-up page.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ovfauszoweayse2374y.png)

Using shoelace.style's helpful [`<sl-input>` elements](https://shoelace.style/components/input) with built-in input validation for emails, I put together a sign-up page with a component with the following render functions:

```{js sign-up-render}

get _form () {
  return this.renderRoot.querySelector('#signupform')
}
  
handleSubmit (event) {
  let email = this._form.querySelector('#email').value
  let password = this._form.querySelector('#password').value
  this.createAccount(email, password)
  this.signIn(email, password)
}

render() {
  return html`
      <div id="signupform">
          <sl-input id="email" label="Email" type="email" placeholder="Enter email here" clearable></sl-input>
          <sl-input id="password" label="Password" type="password" toggle-password placeholder="Enter password here"></sl-input>
          <br>
          <sl-button @click=${this.handleSubmit}>Submit</sl-button>
      </div>
  `;
}
```

Where `createAccount()` and `signIn()` use the [Firebase authentication SDK](https://firebase.google.com/docs/auth/web/password-auth). For example:

```
createAccount(email, password) {
  const auth = getAuth();
  createUserWithEmailAndPassword(auth, email, password)
  .then((userCredential) => {
      const user = userCredential.user;
  })
}
```

A couple of interesting things here:

- I linked `handleSubmit()` to the click event of the `<sl-button>` element
  using `@click=${this.handleSubmit}`. For more information, see the lit documentation on [*adding event listeners in the element template*](https://lit.dev/docs/components/events/#adding-event-listeners-in-the-element-template).
- `_form` acts as a way to make the child `<div>` element accessible throughout the component and to parent components. This technique is also documented in the lit documentation. See [*accessing nodes in the shadow DOM*](https://lit.dev/docs/components/shadow-dom/#accessing-nodes-in-the-shadow-dom).
  
--- 

## Conclusion
At the end of this update I have a working way for users to sign up! I had hoped to have a working storage solution by this point but I am happy with the progress I have made. I think the time I have invested into setting up Firebase will pay dividends in future when it comes to integrating with other services.

Only time will tell! Make sure you follow me to find out! Or catch up on my personal site where these blogs are posted first: [adamjhawley.com](adamjhawley.com)