How to use Firebase Firestore as a database

A tutorial on setting up Firestore as a database, this is a very convenient solution to the storage problem!

I need to create storage for some of my dataMembership club, Where I teach programming.

I want my users to be able to manually say "I have completed this course" by clicking a button.

Basically, I want to store a specific object for each user.

Set up Firebase

I decided to useFirebaseFor this, especiallyFirestore databaseThey provide.

Its free tier is very spacious and can store up to 1GB of data and 10GB of network transmission per month. Far beyond my estimation of what I need!

Open the Firebase website athttps://firebase.google.com/

Firebase is a Google product, so once you log in to Google, you will actually be logged in to Firebase as well.

I created a new Firebase project by clicking "Create Project".

I gave it a name:

That's it:

I clicked the "Web" icon next to iOS and Android, and entered the app name:

Firebase immediately gave me the required access key, as well as some sample code:

After that, Firebase prompted me to add some security rules to the database.

By default, you can choose two things: open to everyone or closed to everyone. I started to open it up to everyone, they called itTest mode.

That's it! I am ready and created a favorite.

What are collectibles? In Firestore terminology, we can create many different collections and assign documents to each collection.

The document can then contain fields and other collections.

It is not much different from other NoSQL databases, for exampleMongoDB.

I highly recommend watchingYouTube playlists on the subject,well-done.

So i added my collectionusers.

I want to use a special string to identify each user, I call itid.

Front-end code

We now enter the JavaScript part.

In the footer, I included these two files provided by Firebase:

<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-firestore.js"></script>

Then I added aDOMContentLoaded event listenerTo ensure that the code is run when the DOM is ready:

<script>
document.addEventListener('DOMContentLoaded', event => {

}) </script>

In it, I added the Firebase configuration:

const firebaseConfig = {
  apiKey: "MY-API-KEY",
  authDomain: "MY-AUTH-DOMAIN",
  projectId: "MY-PROJECT-ID"
}

I pass this object tofirebase.initializeApp()And then I callfirebase.firestore()Get references to database objects:

firebase.initializeApp(firebaseConfig)
const db = firebase.firestore()

Now, I created a script that uses a simple loop to populate the user ID from the list owned by the backend:

const list = [/*...my list...*/]

list.forEach(item => { db.collection(‘users’).doc(item).set({}) })

.. Then I ran it once to populate the database. I basically programmaticallyCreate a document for each user.

This is very important, because once the documents are created, it means that I can restrict the permissions to update only those documents, and not allow adding new documents or deleting them (we will do this later)

Okay, now I have some complicated logic to identify the user ID and the course ID, I will not repeat it because it has nothing to do with the task here.

Once this information is collected, a reference to the object can be obtained:

const id = /* the user ID */
const course = /* the course ID */
const docRef = db.doc(`membership/${id}`)

great! Now I can get the documentation reference from Firebase:

docRef.get().then(function(doc) {
  if (doc.exists) {
    const data = doc.data()
    document.querySelector('button')
      .addEventListener('click', () => {
      data[course] = true
      docRef.update(data)
    })
  } else {
    //user does not exist..
  }
})

My logic is actually much more complicated, because I have other active parts, but you get the idea!

I initialize the document data by callingdoc.data()Then, when I click the button (I assume it is the button that says "I have completed the course"), we willtrueThe Boolean value of the club ID.

Later, when the course list page is subsequently loaded, if the course is completed, I can initialize the page and assign the course as follows:

for (const [key, value] of Object.entries(data[course])) {
  const element = document.querySelector('.course-' + course)
  if (element) {
    element.classList.add('completed')
  }
}

Permissions issue

I started Firebase in test mode, remember? This makes the database open to everyone-everyone has access keys, and these access keys are public and published in the code that is published to the front end.

Therefore, I have to do one thing: determine the level of permission allowed.

I stumbled upon a very important question.

Using the Firebase console, inrule, We can adjust the permissions. Initially, this was the default rule:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

I changed the rule toread, update, So you can only update the document, but not create a new document:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, update;
    }
  }
}

But I can't stop people from using the Firebase API (now available for free in the browser) to browse and list all other documents in the collection so that they can access other people's files.

Although this cannot handle any sensitive data, this code cannot be delivered.

Move code from front-end to back-end via custom API

The licensing issue is a roadblock.

I considered deleting all the code, but in the end I found that I can completely hide all API access in the browser and use the Node.js service to run the Firebase API.

This is also a common way to hide the private keys/private keys needed for services: hide them behind the servers you control.

Instead of calling Firebase from the browser, I created a set of endpoints on my own server, for example:

  • Publish to/courseSet course as completed
  • Publish to/dataGet data associated with the user

I useExtract API:

const options = {
    method: 'POST',
    headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
    },
    body: JSON.stringify({ id, course, lesson })
}
const url = BASE_URL + '/lesson'
fetch(url, options).catch(err => {
    console.error('Request failed', err)
})

All the logic with clicking the button and so on is kept in the client code, of course, I just moved the Firebase logic away.

On the Node.js server side, I installed the officialfirebasePackage usenpm install firebaseAnd request it:

const firebase = require('firebase')

I built aMeansServer to be usedCORSI initialized Firebase:

const firebaseConfig = {
  apiKey: process.env.APIKEY,
  authDomain: process.env.AUTHDOMAIN,
  projectId: process.env.PROJECTID
}

firebase.initializeApp(firebaseConfig) const db = firebase.firestore()

Then the code is exactly the same as the one I used in the front end, except that it can now be triggered on HTTP endpoint calls. This is the code to return a specific document from our collection

const getData = async (id) =>  {
  const doc = await db.doc(`membership/${id}`).get()
  const data = doc.data()
  if (!data) {
    console.error('member does not exist')
    return
  }
  return data
}

app.post(’/data’, cors(), async (req, res) => { const id = req.body.id if (id) { res.json(await getData(id)) return } res.end() })

This is the API to set the course as completed:

const setCourseAsCompleted = async (id, course) => {
  const doc = await db.doc(`membership/${id}`).get()
  const data = doc.data()
  if (!data) {
    console.error('member does not exist')
    return
  }
  if (!data[course]) {
      data[course] = {}
  }
  data[course]['done'] = true
  db.doc(`membership/${id}`).update(data)
}

app.post(’/course’, cors(), (req, res) => { const id = req.body.id const course = req.body.course if (id && course) { setCourseAsCompleted(id, course) res.end(‘ok’) return } res.end() })

That's basically it. More code is needed to handle other logic, but the gist of Firebase is the code I posted. Now, I can also add a user to my server-side service and restrict all other access to the Firebase API and enhance its security.


More service tutorials: