A couple of weeks ago I decided that I wanted to build and launch a new project in a few days. To achieve that, I'd need to simplify as much as possible my tasks so I thought it was the perfect moment to learn Firebase and use it in a project for the first time. The project is still under development but so far I've learnt a few lessons I'd like to share.
Why should you use Firebase?
The main reason I had to use Firebase was curiosity. I've been wanting to try it for a while now and with the launch of AWS Amplify (which is pretty similar) my curiosity kicked in again. But other factors can make you decide to choose Firebase. For example:
Free plan: The free tier is good enough to build and run a small side project. It will give you 50k document reads, 20k document writes 20k document deletes, 1GB of stored data and 10GB of network. See free plan details
Fast developing experience: writing an app from scratch requires a lot of time if you're writing every single piece of code. Just all the authentication workflows can take you a week or more so having all that built out of the box is a huge plus. With Firebase I just had to install a dependency in my front end, and forget about any back end code for authentication, APIs or data storage etc. The only thing I've had to write is Firestore rules (the ones used to control who can do what in your database) and those are super simple to use.
Documentation: the official docs are great and even include some Youtube video series like this one for Firestore. Also, there are tons of articles and videos on Youtube. My favourite is probably the Fireship.io channel.
Super simple deployments: With the Firebase CLI, deploying a Firebase project is as simple as running
firebase deploy
. No need to set up webhooks, clone your repo or anything like that. Just running a script and seeing your project live on a .web.app domain, even with SSL enabled is awesome.
My 8 tips when working with Firebase
I hope you find the reasons above enough compelling to try Firebase but before that, let me tell you a few tips that I think would make your project development event better:
Use the Firebase CLI and VSCode extensions
You can install the CLI running npm i firebase-tools -g
and then authenticate running firebase login
with your Google credentials (did I mentioned Firebase is owned by Google?). In addition, the two VSCode extensions I installed are Firebase Explorer and Firestore Rules.
Create two Firebase projects
In order to keep your develop and production environments completely isolated, I'd create two different projects in Firebase (for example myAwesomeApp and myAwesomeApp-dev). Each project will have its own database, hosting and, more important, its own quotas so all the tests you'll do will not affect your live environment. You can create the project using the Firebase CLI or, better, create them manually in the Firebase Console website.
Vuejs + Firebase project scaffold
As mentioned earlier, the project I'm creating is a web built with Vuejs so to start I ran vue create my-project-name
. Then inside the project folder, run firebase init
and selected the features you want, like Hosting or Firestore . Next, choose the development project you created in the previous step and finally, the CLI will ask you for the files where it'll define the Firestore rules and indexes. Once your project is scaffolded, you can do your first deployment!
Setup deployment scripts for each environment/project
Once your Firebase project is initialized, you can deploy it running firebase deploy
. This is ok to deploy to the Firebase project you chose when you initialized the project, but as we want to target different projects (remember we have develop and production), I suggest to create different scripts in your package.json file. Here are the ones I have:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"deploy-rules-dev": "firebase deploy --project myAwesomeApp-dev --only firestore:rules",
"deploy-rules-production": "firebase deploy --project myAwesomeApp --only firestore:rules",
"deploy-functions-dev": "firebase deploy --project myAwesomeApp-dev --only functions",
"deploy-functions-production": "firebase deploy --project myAwesomeApp --only functions",
"deploy-dev": "vue-cli-service build --mode development && firebase deploy --project myAwesomeApp-dev",
"deploy-production": "vue-cli-service build --mode production && firebase deploy --project myAwesomeApp"
},
As you can see the firebase CLI has different flags we can use:
- --project is used to select our target project
- --only is used to select which part of our project we want to deploy.
Use environment variables
This is very obvious but you should use environment variables to load you Firebase project keys or other variables that would be different in each environment. For example, initialise your app like this:
// βοΈ DONT
const firebaseApp = firebase.initializeApp({
apiKey: 'xxxXXXXXxxXXXXxxXXXXxxxx',
authDomain: 'xxxXXXXXxxXXXXxxXXXXxxxx',
databaseURL: 'xxxXXXXXxxXXXXxxXXXXxxxx',
projectId: 'xxxXXXXXxxXXXXxxXXXXxxxx',
storageBucket: 'xxxXXXXXxxXXXXxxXXXXxxxx',
messagingSenderId: 'xxxXXXXXxxXXXXxxXXXXxxxx',
appId: 'xxxXXXXXxxXXXXxxXXXXxxxx',
measurementId: 'xxxXXXXXxxXXXXxxXXXXxxxx',
})
// β
DO
const firebaseApp = firebase.initializeApp({
apiKey: process.env.VUE_APP_APIKEY,
authDomain: process.env.VUE_APP_AUTHDOMAIN,
databaseURL: process.env.VUE_APP_DATABASEURL,
projectId: process.env.VUE_APP_PROJECTID,
storageBucket: process.env.VUE_APP_STORAGEBUCKET,
messagingSenderId: process.env.VUE_APP_MESSAGINGSENDERID,
appId: process.env.VUE_APP_APPID,
measurementId: process.env.VUE_APP_,
})
In my case, I'm using Vuejs so I just need to create two files named .env.development and .env.production locally and whenever I run npm run build
, it will automatically replace the environment variables with the values from the correspondent file. You can read more about Vuejs environment variables here.
Think twice your data model and don't be afraid to duplicate
Before you start coding, think about how your app is going to look like, which data you are going to need in each page and which pages are going to be more used. This is pretty important because it will affect the way you'll store your data in Firestore (the noSQL database used in Firebase) or the Real Time Database.
As one of the limitations of the free tier is the number of documents your app reads and writes, consider doing it just when you need it.
One of the things that have made me save a ton of document reads is duplication of some fields. This is something not very common in relational databases (I'd say it's even forbidden π ) where we use foreign keys and join queries but it's pretty normal in noSQL databases. You can read more about data modelling and view some videos in this section of the docs.
Create functions for your Firestore rules
Once you start defining Firestore rules, there are two functions that you'll use all the time:
- validate if the request comes from a logged user
- validate if the user accessing a document is the one who created it
For that, you can create the following functions in your firestore.rules file:
//**** Functions ****//
function isLoggedIn(){
return request.auth != null;
}
function isOwner(){
return request.auth.id ==resource.data.uid;
}
You can find more info about security rules here.
Paginate and limit your queries
This comes back to the limitations of the free tier. Just remember to add a limit(x) to your collection queries whenever you are going to access your data. You don't want to return 150 documents when on your page you can only display 20.
Pagination is super simple to build thanks to the startAfter() method. Find below an example of how I'm doing pagination in my Vuejs app:
// part of store/index.js file
// global variable to store last paginated element
let paginationLast = null
// Vuex store action
getUpcomingTalks({ commit }, payload) {
return new Promise((resolve, reject) => {
talksCollection
.orderBy('date', 'asc')
.limit(payload.limit || 12)
.startAfter(paginationLast)
.get()
.then((res) => {
if (res.docs.length > 0) {
// save last item for pagination
paginationLast = res.docs[res.docs.length - 1]
commit('GET_UPCOMING_TALKS', res.docs)
return resolve()
} else {
reject({ hasMore: false })
}
})
.catch((err) => {
console.log('err in action :>> ', err)
return reject()
})
})
},
Just remember:
- limit will limit the number of documents returned, pretty straight forward
- startAfter will tell Firestore what is the latest document you queried before. In my case, the first time I'll send it null, so it will start at the beginning of the collection. Then after each successful query, I update it with the last item so the following queries will start from it. Note that this has to be a document reference, not an id.
Conclusion
I still have a ton of things to learn about Firebase but I'd say these are the more important things I've learnt so far. Hope you find them useful.
If you liked this article, you can follow me on Twitter where I share dev tips an insteresting articles and updates about the progress of my projects π€
Oh! and in case you're wondering, the project I'm building with Firebase is QuickTalks.io a place to organise and find talks for small audiences.
This article was originally posted in my blog where you can find other articles about web development focused on Laravel, Node.js Vue and more.
Top comments (2)
Nice post! I absolutely love firebase, a few tips from me:
Thanks for sharing those. I haven't used the emulators that much but I agree for testing functions they'll be super useful. I can tell you the amount of times I've deployed a Firestore triggered function just to get it right π