Build a Serverless Online Quiz with Google Cloud Functions and Cloud Firestore

Romin Irani
Romin Irani’s Blog
10 min readMay 16, 2018

This post will take a look at how you can build a Serverless online quiz using a couple of services on the Google Cloud Platform, namely Cloud Functions and Cloud Firestore.

If you are new to Google Cloud Functions, I suggest that you take a look at my tutorial series on Google Cloud Functions. It gets you up-to speed on the basics, types of functions and there is a Github repository to support it too.

Cloud Firestore, as the documentation states, is a NoSQL document database built for automatic scaling, high performance, and ease of application development. It is currently in Beta and I decided to give it a go and demonstrate how we could power some of our functionality wrapped inside Google Cloud Functions via a persistence layer in Cloud Firestore.

Note that I could have used Firebase Functions too, but I prefer to show this via the plain vanilla Cloud Functions in Google Cloud Platform.

Quiz in Action

Before we jump into how we build the online quiz, it is nice to see it in action. Since we are powering our quiz via Cloud Functions, all we will have is a HTTP URL to launch in the browser that will trigger the Cloud Function.

Assuming that you will visit that URL, you will get a screen that looks like this in your browser. The quiz that we have created is “Countries and their Capitals”. A sample screen is shown below:

Screen 1 : Quiz Question

You can select one of the options as the answer and click on submit. It will provide you with the results as shown below, along with an option to continue taking the quiz.

Screen 2 : Results of the Quiz Question and option to take another Quiz Question

That is all there is to it but you get the picture. All of the above will be driven via Cloud Functions and the set of quiz questions, along with the 4 options and answer will be there in Cloud Firestore. No servers to run for you at all.

Let’s get started.

Setting up Google Cloud Platform Project

Here are a few steps to complete to begin our journey:

  1. Our first step is to ensure that we have a Google Cloud Platform (GCP) account with billing enabled.
  2. Create a GCP Project that will host our Cloud Functions and will be enabled for Firestore.
  3. I will also assume that you have Google Cloud SDK installed locally on your system. I will demonstrate a few commands on how to deploy your Cloud Functions from your local workstation.
  4. Enable Google Cloud Functions API for your GCP Project.

If you would like to see detailed screenshots on how to do the above, please follow my tutorial on Setting up a Local Environment for Google Cloud Functions development, which is part of my Google Cloud Functions tutorial series.

Now that we have the setup in place, let us move to the first part of our Online Quiz application, a place to hold our questions and answers or in other words, build our bank of questions and understanding how we can invoke it via any other application.

Firestore

Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development.

Having said that, the best way of think about it is a document store. Think of a document store as a collection of different kinds of documents or types. Examples of kinds or types can be a questions that will hold a list of question or employees, which will hold a list of employees.

So there are 3 things to keep in mind, if I have to simplify : Collection, Document and Attributes (Properties). So a quiz can be modelled as follows:

  1. Collection name is questions.
  2. Document name is question.
  3. Each Document i.e. question will have the following properties (assuming that it is a multiple-choice question with one correct answer).
    - id
    - title
    - option1
    - option2
    - option3
    - option4
    - answer

Now that we have got that out of our way, let us go setup Firestore for our project.

Setting up Firestore for your GCP Project

The first step is to visit the Firebase Console and sign-in with the same account. Once signed in, click on Add Project as shown below:

This will bring up a screen, where you should select the same GCP Project that you created in the earlier section and then click on ADD FIREBASE button as shown below:

This completes your step to add Firebase database to your GCP Project. It will lead you to the Firebase console as shown below:

But we are not done yet since you have both Firebase and Firestore and by default, this project adds Firebase to your project, whereas we need Firestore.

To do that, click on the Database link, once you are logged in. You will find that in the Develop → Database section in the left menu, as shown below:

This will show you that Firebase database is selected. What we need to do is select the Firestore database. Click on the Database dropdown list at the top and select Cloud Firestore BETA as shown below:

Wait for the initializations to get done. At the end , you should see the Cloud Firestore BETA selected for your project as shown below:

Creating the Collection and sample documents

Now that we have our database selected, it is time to create our Collections as described earlier.

The Cloud Firestore documentation is excellent and I suggest to read that, but for now, our minimal understanding from the earlier section will be enough.

To re-iterate, our collection will be named questions. This collection will contain documents, where each document will contain the question details (Attributes/properties) that we explained earlier.

We will create our collection and add a few sample questions as given below. This can be done via the client libraries too, but we will keep with the web console for now.

Go ahead and click on ADD COLLECTION as shown below:

This will bring up a form where you can give a name for your collection. In our case we will call it questions.

Click on NEXT. This step will give us the opportunity to also add our first record i.e. document.

As you can see, we can give a unique id to help identify our document within the collection. We can either go with the AUTO-ID presented over here or we can click on that and give our own generated id. In my case, I will go with unique ids that are in sequential order like 1,2,3… and so on.

Once you provide your id, you can then start creating the fields. Remember that each question i.e. document of ours has the following fields:

  1. title : This is the question in the quiz.
  2. option1, option2, option3 and option4 : These are 4 options out of which, only one answer is correct.
  3. answer : This contains either 1 or 2 or 3 or 4, which indicates the correct answer for the question.

Here is a sample screen that shows the 2 questions (documents) that I created in the collection with the fields for the second question shown:

Go ahead and create as many questions as you would like. For the purpose of our demo, 2 or 3 questions in total will suffice.

Great ! We now have our Firestore collection ready for serving to applications. We shall be using the Node.js Firebase npm modules to access them from our Cloud Functions as demonstrated in the next section.

Writing our Cloud Functions

Let’s move now to the final puzzle in our project i.e. write Google Cloud Functions to serve a random question to the user and check the answer. In short, we shall be writing two functions:

  1. getRandomQuestion
  2. checkanswer

The names of the functions are self-explanatory.

getRandomQuestion

This Cloud Function will get a random question from the Firestore collection i.e. questions. It will serve a HTML form with the question and the 4 options. The form will then on submit action trigger the checkanswer function.

checkanswer

This function will check the answer and inform the participant if the answer is correct or not. It will also put in a link to the getRandomQuestion again, so that the participant can continue to take more quiz questions.

On your local machine, create a folder in which our files will result. Inside that folder, create a index.js file with the code shown below:

Here are a few comments on the code above:

  1. We initialize the Firebase database in the first few lines.
  2. There are 3 HTML templates that are created. One for the question and the other two for the right and wrong answer response. Notice that the question_template just creates a HTML form, which will show the question , the 4 options in a drop-down list and a Submit button, which will submit the question id (hidden) and the response i.e. answer to the 2nd Cloud Function available at the /checkanswer endpoint.
  3. Please bear with me that I created only 2 questions in my Firestore questions collection and instead of doing a count of the number of questions, I have hard coded the countQuestions variable to a value of 2.
  4. The getRandomQuestion function gets a random id, which is one of the document ids that uniquely identifies a document in the collection (questions). We using the Firestore API to retrieve the specific document in the collection via the DocumentRef ‘s get() method. Once we have the Document, we can then get it’s fields and pass them to the template function. The template function returns back the HTML that is then sent back to the browser via the ExpressJS response objects send method.
  5. Similarly, the checkanswer method is straight forward too. We first extract out the id and answer fields from the ExpressJS request object. We retrieved the Document again from the Firestore collection and compare the answer. Depending on whether the answer is right or wrong, we use the appropriate template. Notice that in the wrong_answer_template, we return a link to the getRandomQuestion again , so that the user can try answering another question and continue that way.

The package.json file is shown below. Place it in the same folder as the index.js file.

{
"name": "firestore-function",
"version": "1.0.0",
"dependencies": {
"firebase-admin": "5.12.0",
"firebase-functions": "1.0.2"
}
}

To deploy the function, launch a terminal session and go to the folder in which the index.js and package.json files reside. Assuming that you installed the local gcloud tools and the beta dependencies, you can deploy the two functions via the two commands as shown below:

$ gcloud beta functions deploy getRandomQuestion --trigger-http$ gcloud beta functions deploy checkanswer --trigger-http

Ensure that both the functions are deployed correctly via the command shown below:

$ gcloud beta functions list
NAME STATUS TRIGGER REGION
checkanswer ACTIVE HTTP Trigger us-central1
getRandomQuestion ACTIVE HTTP Trigger us-central1

Since both of these functions are HTTP Trigger based functions, we can get their HTTP endpoint via the describe command as shown below. Remember that we just need the HTTP Trigger Endpoint url for the getRandomQuestion function . The property httpsTrigger is important.

$ gcloud beta functions describe getRandomQuestionavailableMemoryMb: 256
entryPoint: getRandomQuestion
httpsTrigger:url: https://us-central1-<YOUR_PROJECT_ID>.cloudfunctions.net/getRandomQuestion....status: ACTIVE
timeout: 60s
updateTime: '2018-05-16T00:51:31Z'
versionId: '2'

Copy the value of the url and you are all set to invoke that from the browser and see it in Action as given below:

Screen 1 : Quiz Question

Enjoy your own Quiz ! :-)

Monitoring

You can monitor your Cloud Functions via Stackdriver Logging. It is integrated into both the Google Cloud Console and also Firebase console.

From Google Cloud Console, you can visit Stackdriver Logging and can see the Cloud Functions execution as shown below:

Alternately, if you prefer the Stackdriver logging to be visible via the Firebase console, go to Functions option in the main menu as shown below:

You can then click on the Logs tab, to see the Functions execution and any log statements for your function execution.

Ideas to work on

This concludes our tutorial on building out a serverless online quiz application via Cloud Functions and Cloud Firestore. There are definitely improvements that we can make to this and here are a few suggestions to work on:

  1. Instead of hardcoding the count for quiz questions in our Cloud Functiosn code, we could possibly do a count of the records in the database. This can prevent a redeploy of the function in case you keep adding new questions to your bank of questions.
  2. Instead of using the Firebase console to add records, you could dynamically add new questions by creating a comma separated list of questions that can be uploaded to Cloud Storage. You could then trigger a Cloud Function, that can read the file and use the Firestore API to dynamically add more records (documents) to the collection. Take a look at how you can write Background Cloud Functions, especially one that is triggered by a file upload to Cloud Storage.

Wrapping up

Hope this gives you a good idea on how to use Cloud Functions along with Firestore. Thank you for reading this tutorial. Hope you enjoyed it. If you have any feedback, please let me know.

--

--

Romin Irani
Romin Irani’s Blog

My passion is to help developers succeed. ¯\_(ツ)_/¯