DEV Community

Cover image for To the Stars with Quasar & Firebase - Initial Service & Structure
Adam Purdy for Quasar

Posted on • Edited on

To the Stars with Quasar & Firebase - Initial Service & Structure

Table Of Contents

  1. Introduction
  2. Installation
  3. Firebase Configuration and Application Environment
  4. Structure
  5. Base Service
  6. Summary
  7. Repository
  8. About Quasar

1. Introduction

Firebase is Google's mobile platform that helps you quickly develop high-quality apps and grow your business. It helps you by allowing you to build apps fast without managing infrastructure and gives you functionality like analytics, databases, messaging, and crash reporting so you can move quickly and focus on your users.

It was initially created as a startup in 2011 and was publicly available in 2012. In October 2014, Google acquired Firebase, which evolved into a platform that has 18+ products and is currently being used by 1.5 million apps as of October 2018. source - Wikipedia.

This article intends to be a base source for the articles that follows it, as the nature of this series is to inform the developer on how to set up Firebase within the Quasar Framework. While this information intends to be a foundational step for getting started with Firebase inside of Quasar, this is just the surface and is highly advisable to always consult the Firebase documentation initially for guidance.

  • 1.1 Assumptions

Before we get started, a few assumptions are in order. Unless otherwise specified in the posts to follow in this series, a presumption of the Quasar project consists of using: Stylus, auto importing of components, and using the features of ESLint and Vuex.

Alt Text

Also, please be sure to keep your quasar-cli as current as possible.

$ npm i -g @quasar/cli@latest
Enter fullscreen mode Exit fullscreen mode

Also, if you're here you've done two things:

  • Chosen the Quasar Framework to be your Vuejs front-end component framework
  • Chosen Firebase at the minimum to handle your Quasar application's authentication... maybe more

And finally, if something is not directly mentioned moving forward, it's more than likely to be covered in either the Quasar docs, or the Firebase docs. An effort will be made to keep a watchful eye on concepts that specifically pertain to the integration of Firebase within the Quasar Framework. Still, if something is missing, there is also the #firebase channel on the Quasar Discord server. A final note, this code is for Vue v2 and Quasar v1.

2. Installation

If you're new to Firebase, you must create a Firebase account with a Google identity and create a project in the console.
Alt Text

Once you've gotten your account and project setup in Firebase, it's time to install dependencies in your Quasar app and start the configuration process.

Install Firebase to your Quasar application, preferably via yarn package manager:

$ yarn add firebase
# or 
$ npm install firebase
Enter fullscreen mode Exit fullscreen mode

A configuration object is needed to configure the Firebase SDK for initialization from the Firebase console.

You can get access to your Firebase config object at any time by:

  • Sign in to Firebase, then open your project.
  • Click the Settings icon, then select Project settings.
  • In the Your apps card, select the nickname of the app for which you need a config object.
  • Select Config from the Firebase SDK snippet pane.
  • Copy the config object snippet, then add it to your app's HTML.

or

  • In your console run: $ firebase apps:sdkconfig web

3. Firebase Configuration and Application Environment

Assuming you are developing an app that will live in a production environment, Firebase recommends creating separate projects in the Firebase console. One for production, one for dev, and even a public testing endpoint if you'd like. The point here is to keep production data safe from development.

The use of environment variables is used during Quasar's build process to identify which Firebase and other configuration information are needed given the desired build. Earlier in this article's history, the use of Qenv was used but has since become deprecated. Dotenv is now used to set up the application's environment variables.

yarn add -D dotenv
# or
npm install -D dotenv 
Enter fullscreen mode Exit fullscreen mode

Create a .env file per the instructions on their repository. This file will house all environment options based on your application needs. Following dotenv's suggestion, do not split up your .env files per environment. Here's what your .env file could look like.

Note - For Windows Users - You'll need to install an additional package called cross-env, and update your package.json script.

{

  "scripts": {
    "dev:win": "cross-env QENV=DEV quasar dev"
  }

}
Enter fullscreen mode Exit fullscreen mode

/.env

# DEVELOPMENT FIREBASE CONFIG
DEV_API_KEY=######
DEV_AUTH_DOMAIN=######
DEV_DATA_BASE_URL=######
DEV_PROJECT_ID=######
DEV_STORAGE_BUCKET=######
DEV_MESSAGING_SENDER_ID=######
DEV_APP_ID=######
DEV_API_ENDPOINT=######

# STAGING FIREBASE CONFIG
STAGE_API_KEY=######
...
Enter fullscreen mode Exit fullscreen mode

Take notice of the prefix, 'DEV', in the variable names. This will be used in our script to set our QENV variable.

Create or update your build script in your package.json file.

/package.json

{
  ...
  "scripts": {
    "dev": "QENV=DEV quasar dev",
    "lint": "eslint --ext .js,.vue src",
    "test": "echo \"No test specified\" && exit 0"
   },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Per the Quasar documentation, we need to add our parsed values from our .env file. We also need to add our environment variables set during our build script to allow our config file to determine which config object to use.

/quasar.conf.js

const enviromentConfiguration = require('./src/utils/environmentConfig.js')
module.exports = function (ctx) {
  return {
    ...
    env: {
      QENV: enviromentConfiguration(process.env.QENV)
    }
    ...
Enter fullscreen mode Exit fullscreen mode

Finally, create a utils folder and create an enviromentConfig.js file.
This file takes our environment variable set in our build script that gets passed over from our quasar.config.js and uses it to determine which configuration will initialize the Firebase applications. Utilizing template string literals and aligning your environment variable names with the prefixes in your .env file.

/src/utils/environmentConfig.js

const ENV = require('dotenv').config().parsed
/*
  Use an environment variable set in package.json scripts to determine
  the applications runtime environment. Add more switch cases as
  need for additional environments. Remember, Firebase recomends supporting
  separate Firebase project for different application environments: httpsq://firebase.google.com/docs/projects/multiprojects#support_different_environments
*/

module.exports = (QENV) => {
  if (!['DEV', 'STAGE', 'PROD'].includes(QENV)) {
    throw Error('Unknonw or not supplied environment variable')
  }
  return {
    FIREBASE_CONFIG: {
      apiKey: ENV[`${QENV}_API_KEY`],
      authDomain: ENV[`${QENV}_AUTH_DOMAIN`],
      databaseURL: ENV[`${QENV}_DATA_BASE_URL`],
      projectId: ENV[`${QENV}_PROJECT_ID`],
      storageBucket: ENV[`${QENV}_STORAGE_BUCKET`],
      messagingSenderId: ENV[`${QENV}_MESSAGING_SENDER_ID`],
      appId: ENV[`${QENV}_APP_ID`]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Structure

Before we dive into setting up the code, let's take a second to illustrate and discuss a bit of the opinionated direction we'll be taking. The fact that we can simply import Firebase and all its needed services into a single boot file, or just import the service directly in our Vue file and build our functionality as needed is definitely an option moving forward. But, the attempt here in these next few articles is to guide the development into more of a separated and maintainable production approach. We want to separate our services by a single namespace, firebaseServices, and also have each contextual service in a file. We can lend our structure to small and manageable files, as well as writing tests.

Alt Text

We'll revisit this again as we add more contexts to the application.

The following code snippet is meant to highlight the initial approach of setting up your application by separating your server connection and the actual service itself. The next bits are not intended to set up any real functionality, but highlight the first necessary pieces of the application structure. A typical Quasar app structure is here.

5. Base Service

The first place to get Firebase into Quasar is starting in a boot file. Create a boot file, then add its name to the boot array in the quasar.conf.js. Be sure to read up on Quasar’s boot files here. It is a great review, and also the first place to start when debugging Firebase issues.

$ quasar new boot firebaseConnection
Enter fullscreen mode Exit fullscreen mode

/quasar.config.json

boot: [
  'firebaseConnection'
],
Enter fullscreen mode Exit fullscreen mode

Our boot file is not the Firebase service itself, but a point in which to bring in the Firebase service, in case you need or want to switch out your backend to another cloud provider or traditional backend API interface.

/src/boot/firebaseConnection.js

import firebaseServices from '../services/firebase'

export default async () => {
  const app = firebaseServices.fBInit(process.env.QENV.FIREBASE_CONFIG)

  // Validation that our service structure is working
  // with an initialize Firebase application and auth instance.
  console.log('Firebase App Instantiation:', app)
  console.log('Firebase Auth Module:', firebaseServices.auth())
}
Enter fullscreen mode Exit fullscreen mode

Calling the fBInit(process.env.QENV.FIREBASE_CONFIG) method is just a validation that the service structure is working, but does not guarantee that the initialization happened with a valid API key.

Create a new directory in our application structure called services, and put the base service inside.

/src/services/firebase/base.js

Throughout the next couple of articles, there will be a separation of each firebase service in relation to its context. These service file will be then be pulled into the /src/services/firebase/index.js file where we can have a top-level firebaseServices namespace via Object.assign().

Have a look here: /src/services/firebase

import firebase from 'firebase/app'
import 'firebase/auth'

/**
 * Returns Firebase's auth service
 * https://firebase.google.com/docs/reference/js/firebase.auth.html#callable
 * @returns {Auth} - The Firebase Auth service interface
 */
export const auth = () => {
  return firebase.auth()
}

/**
 * Convenience method to initialize firebase app
 * https://firebase.google.com/docs/reference/js/firebase?authuser=1#initializeapp
 * @param  {Object} config - FIREBASE_CONFIG during the build process
 * @returns {App} - Creates and initializes a Firebase app instance.
 */
export const fBInit = (config) => {
  return firebase.initializeApp(config)
}

Enter fullscreen mode Exit fullscreen mode

First, we import our Firebase SDK and separate the imports per Firebase product. Be sure to check out the complete list of products, click on the expansion item for "Available Firebase JS SDKs (using bundler with modules)". Likewise, we could import the entire SDK, but you can shave off KB's by importing the products separately. Also, keep in mind if you only import just the app portion of the firebase and NOT the other products you'll end up getting undefined errors when trying to work with those products.

The fBInit method allows Firebase to register a project that was created in the Firebase console.

Run the app:

$ yarn dev
Enter fullscreen mode Exit fullscreen mode

Remember if you're running Windows

$ yarn dev:win
Enter fullscreen mode Exit fullscreen mode

Once the app is finished building, it will launch to http://localhost:8080/#/.

If you have successfully instantiated the Firebase service you will see this:
Alt Text

Otherwise, your application will fail to initialize and you will have a Quasar boot error and a Firebase error message stating you have an invalid key.
Alt Text

The Firebase API key doesn't get validated until an authentication method is executed. This will be highlighted in the Email Authentication article.

6. Summary

Here is the start of our integration between Firebase into the Quasar Framework and highlights getting a project created in the Firebase console, setting up the basic structure for our firebaseConnection and our base Firebase service files. This base service is ready for future application needs. It will serve as a starting point for any Firebase type of authentication or any of the other services that Firebase offers.

7. Repository

Quasar-Firebase Repo: Base Service

Up next: Email Authentication

8. About Quasar

Interested in Quasar? Here are some more tips and information:

More info: https://quasar.dev
GitHub: https://github.com/quasarframework/quasar
Newsletter: https://quasar.dev/newsletter
Getting Started: https://quasar.dev/start
Chat Server: https://chat.quasar.dev/
Forum: https://forum.quasar.dev/
Twitter: https://twitter.com/quasarframework
Donate: https://donate.quasar.dev

Top comments (15)

Collapse
 
ezanglo profile image
Ezra Anglo

Hi! Thanks for the article! I'm always using this to start a new firebase quasar app. I see that you updated with dotenv instead of qenv. But I encountered problems with it or with the way the scripts are built. I reinstalled cross-env and used that in my script so now it "cross-env QENV=DEV quasar dev" again and it magically works. Hope you put back that part of the article again and hope this helps any newcomers that might be having the same problem

Collapse
 
adamkpurdy profile image
Adam Purdy

Thank you for pointing that out. I'll update the article and repo.

Collapse
 
bgrand_ch profile image
Benjamin Grand • Edited

Thanks a lot!

Suggestion for env:

package.json

{
  "scripts": {
    "dev": "cross-env NODE_ENV=staging quasar dev",
    "staging:build": "cross-env NODE_ENV=staging quasar build",
    "production:build": "cross-env NODE_ENV=production quasar build"
  }
}
Enter fullscreen mode Exit fullscreen mode

env-loader.js

const dotenv = require('dotenv')

const envParsed = dotenv.config().parsed
const separator = '_'

module.exports = NODE_ENV => {
  const nodeEnv = NODE_ENV.toUpperCase()
  const envRenamed = {}

  for (const env in envParsed) {
    const name = getEnvName(env)
    const fullName = `${nodeEnv}${separator}${name}`
    const value = envParsed[fullName]

    envRenamed[name] = value
  }

  return envRenamed
}

function getEnvName (env) {
  const regex = new RegExp(`${separator}([A-Z${separator}]+)`)

  return env.match(regex)[1]
}
Enter fullscreen mode Exit fullscreen mode

quasar.conf.js

const envLoader = require('./env-loader')

module.exports = function () {
  return {
    build: {
      env: {
        ...envLoader(process.env.NODE_ENV)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

.env

##
# STAGING
##

# STAGING FIREBASE
STAGING_FIREBASE_API_KEY=test api key staging
STAGING_FIREBASE_APP_ID=
STAGING_FIREBASE_AUTH_DOMAIN=
STAGING_FIREBASE_DATABASE_URL=
STAGING_FIREBASE_MEASUREMENT_ID=
STAGING_FIREBASE_MESSAGING_SENDER_ID=
STAGING_FIREBASE_PROJECT_ID=
STAGING_FIREBASE_STORAGE_BUCKET=

##
# PRODUCTION
##

# PRODUCTION FIREBASE
PRODUCTION_FIREBASE_API_KEY=test api key production
PRODUCTION_FIREBASE_APP_ID=
PRODUCTION_FIREBASE_AUTH_DOMAIN=
PRODUCTION_FIREBASE_DATABASE_URL=
PRODUCTION_FIREBASE_MEASUREMENT_ID=
PRODUCTION_FIREBASE_MESSAGING_SENDER_ID=
PRODUCTION_FIREBASE_PROJECT_ID=
PRODUCTION_FIREBASE_STORAGE_BUCKET=
Enter fullscreen mode Exit fullscreen mode

process.env when staging

FIREBASE_API_KEY=test api key staging
FIREBASE_APP_ID=
FIREBASE_AUTH_DOMAIN=
FIREBASE_DATABASE_URL=
FIREBASE_MEASUREMENT_ID=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
Enter fullscreen mode Exit fullscreen mode

process.env when production

FIREBASE_API_KEY=test api key production
FIREBASE_APP_ID=
FIREBASE_AUTH_DOMAIN=
FIREBASE_DATABASE_URL=
FIREBASE_MEASUREMENT_ID=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
Enter fullscreen mode Exit fullscreen mode
Collapse
 
adamkpurdy profile image
Adam Purdy

Hey Benjamin thanks for your read and suggestion. Just curious, was your suggestion to mainly remove the QENV namespace in the build env, or was there something more that you were highlighting?

Collapse
 
bonatoc profile image
Christian Bonato

Thank you for this.
Just a quick typo: the link to the Boot Files doc doesn't work, as of 2020-12-04. The correct link is: quasar.dev/quasar-cli/boot-files

Collapse
 
stephencarico profile image
Stephen Carico

My quasar.env.json file and package.json scripts are exactly as yours are in your repo, but in my serverConnection.js file this is what worked...

const config = process.env.FIREBASE_CONFIG

Collapse
 
adamkpurdy profile image
Adam Purdy

From the QEnv docs you can set a "Common Root Object". This is what I did for my QEnve installation and is reflected in my code in the serverConnection.js file.

QEvn docs: github.com/quasarframework/app-ext...

Collapse
 
stephencarico profile image
Stephen Carico

Ah. I see what happened. During the QEnv setup I defaulted to "none" when asked, "What name would you like to use for your Common Root Object." After going into my quasar.extensions.json file I was able to switch the common_root_object value to environments. Worked like a charm! Thank you for responding and pointing me to the root of the issue.

Thread Thread
 
adamkpurdy profile image
Adam Purdy

Thank you for posting your question. I added a bit of guidance in that area of the article pointing out that piece of QEnv.

Collapse
 
prototowb profile image
Tobias Rauer • Edited

can this approach be used for firebase 9? Even with importing { initializeApp } and { getAuth } instead, I receive:
[Quasar] boot error: ReferenceError: process is not defined
at Array.WEBPACK_DEFAULT_EXPORT (firebaseConnection.js?4d99:4:1)

EDIT: well, I didn't know that we have to put the env object into the build config object in the quasar config. Just had it in 'return', but it has to be:

return {
...
build: {
...
env: {
QENV: enviromentConfiguration(process.env.QENV)
}
...
}
...

Collapse
 
patratel profile image
patratel

Hey , first of all thank you for posting this guide. It has been very informative. I've gone through it and implemented this login method on my app. Although i stumbled upon an issue that I am unable to resolve. As a matter of fact it is part of the guide, in the 4th section "Otherwise, your application will fail to initialize and you will have a Quasar boot error and a Firebase error message stating you have an invalid key." I've checked the API key a number of times and it does seem to be correct. I cannot figure what do you mean with this step of the guide "The Firebase API key doesn't get validated until an authentication method is executed. This will be highlighted in the Email Authentication article."

Thanks in advance

Collapse
 
patratel profile image
patratel

Ok i found my mistake , i'm gonna drop it here in case anyone else encounters it. I didn't read the QEnv documentation all the way through. Apparently in order to be able to run QENV on Windows you also have to run "npm install --save-dev cross-env" and modify package.json to "dev": "cross-env QENV=development quasar dev"

Collapse
 
bgrand_ch profile image
Benjamin Grand

πŸ”₯πŸ”₯πŸ”₯

Collapse
 
jimoquinn profile image
Jim O'Quinn

You lost me at qenv. Made multiple attempts to get past the 2nd step in the 1st tutorial and quit in frustration.

Collapse
 
adamkpurdy profile image
Adam Purdy

Jim are you familiar with what QEnv is doing and how it is handling the Firebase config object? Be sure to read the QEnv docs as this is a great Quasar application extension that offers the developer to handle our environment configurations during the build process. Feel free to jump into the Quasar discord in the #firebase channel for some clarification and help.