Home / DialogFlow ES / Dialogflow Firebase Tutorial
DialogFlow ES | Firebase

Dialogflow Firebase Tutorial

This website contains affiliate links. See the disclosure page for more details. 

This tutorial was originally published in November 2017, so it is actually more than 2 years old. Cloud Firestore was still in beta when I published it (now it is out of beta). In that time, a lot of things have changed in Firebase, so this may not be the best resource for learning this topic.

I have consolidated the old tutorial into a single page and left the contents mostly as they were.

Obviously this article is due for an update but I am not sure how many people are interested. If I get at least 10 comments requesting me to update this tutorial, I will do it. 🙂

Introduction

This guide will explain how you can use Firebase cloud functions and save data to Cloud Firestore.

What is Firebase?

Wikipedia describes Firebase as a “Mobile and web application platform”, but for our purposes, it is a database to save your chatbot data. Unlike regular databases such as MySQL, Firebase is a document database like MongoDB. If you look at the data inside the Firebase console, you don’t see rows and columns. Rather you see hierarchical JSON.

Dialogflow’s inline webhook editor is deployed on Firebase Cloud Functions

Remember how Wikipedia describes Firebase as a “Mobile and web application platform”? Turns out, there is quite a bit of terminology (some of which you will learn in this guide) when you wish to use Firebase.

Cloud Functions for Firebase are described like this in the documentation:

As the name also hints, this ability to just deploy some code to a server and have the code be executable at any scale is the appealing feature of Cloud Functions.

In the Dialogflow inline webhook, they give us a sample Cloud Function which can be deployed as the webhook for our chatbot.

There is no sample code to save data from the inline webhook

I mention this because like a few other folks, I probably expected that the inline webhook sample code will also provide some ideas on how to do data access.

But without the ability to save data, a webhook is not very useful. So we need to figure out how to use the inline webhook sample code to save data.

Now let us move on to the tutorial.

Create a new Dialogflow agent

We first need to create a Dialogflow agent. You should be able to do it in a single click, but the important thing for this step is that once you have created the agent, you will have a project id.

This project ID will be used for your other work when using Firebase, so make a note of the projectID.

Enable Cloud Firestore for your project

Now you will need to enable Cloud Firestore for your project.

What is the Cloud Firestore?

Firebase already has a real time database. So what is Cloud Firestore?

Here is the official definition:

You might remember from the previous part that the Inline Webhook Editor uses cloud functions. Using the Cloud Firestore, it will be easier to integrate it with our code.

Firebase console

While logged in to your Google account, go to your Firebase console. You should see a screen that looks like below:

Choose the project you created in Dialogflow. Now you should see a screen like below.

Click on Database from the left menu. You will see that there are two options: you can either choose the Realtime Database or the Cloud Firestore.

Click on the “Try Firestore Beta” function. You will see a popup which asks for security permissions.

Keep the default option selected (“Start in locked mode”) and click on the Enable button. You will see a screen which looks a little like below:

In the screen above, click on Add Collection on the left menu.

Important: Name the collection “users” like shown below. We will be referencing this collection by name in a future step, so other names will not work. 

After clicking Next, you will see a screen like below. Click on the AUTO-ID to automatically generate an ID.

Now click on Save (no need to add any values)

You will see an interface with some data added. I have blurred out the data, but you can see that it has changed from the previous screenshot of the database.

Now we have enabled the Cloud Firestore and we are ready to move to the next step.

Initialize Firebase SDK for cloud functions

In this step we will initialize the Firebase SDK for Cloud functions.

First, create a project directory on your local machine. Mine is called FirebaseTestSimple.

In the command line, type the following to install the Firebase CLI (Command Line Interface) [1] using npm.

npm install -g firebase-tools

Now you need to login to Firebase. You can actually do this by typing a command

firebase login

When you do this, you will notice your browser window will open, and you will be prompted to login to your Google Cloud account. Once logged in, you can come back to your Terminal and you will see that you are already logged in to Firebase.

Next you should initialize Firebase functions using

firebase init functions

Two things happen at this step

  1. You must select the associated project ID. You should be able to use the down arrow and the ENTER key to do this
  2. Once the project ID is selected, you will be prompted on “Do you want to install dependencies with npm now?” Select Yes (y)

After you do steps 1 and 2 successfully, you should see some new files in your current directory.

You will also see a folder called functions. Browse into the functions directory and type the following:

npm install firebase-functions@latest --save
npm install -g firebase-tools

See image below for an example:

So what did we just do? We got the latest version of the Firebase functions SDK [2].

Once you do this, you will see a project structure like below:

Source: https://firebase.google.com/docs/functions/get-started#add_the_addmessage_function

You are now ready to move to the next step, where you will make changes to the index.js file and get it ready for deployment.


[1] Read more about the Firebase CLI

Tip: Take some time to read the Project Directories and Deployment sections in the above URL when you get a chance.

[2] Here is how the Firebase functions SDK is defined:

The firebase-functions package provides an SDK for defining Cloud Functions for Firebase.The Firebase SDK for Cloud Functions integrates the Firebase platform by letting you write code that responds to events and invokes functionality exposed by other Firebase features.

Also, please note this message on the Firebase Getting started page.


Adding the code to index.js

In this step, we will be modifying the index.js that you already have inside your functions folder.

If you are completely unfamiliar with Cloud Functions, you should spend at least a little time familiarizing yourself on what they are and how they work. I suggest the following playlist on YouTube, and focus only on the videos which talk about NodeJS. You can follow along the tutorial without watching the playlist, but you will not understand a few things about what we are doing. It is definitely preferable to follow the tutorial after learning more about cloud functions than blindly copy/pasting the code because taking that little time to understand what is happening under the hood will help you a lot as you debug your code.

Open the index.js file in your favorite code editor, and replace it with the following code.

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);


exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    console.log('Request headers: ' + JSON.stringify(request.headers));
    console.log('Request body: ' + JSON.stringify(request.body));

    // An action is a string used to identify what needs to be done in fulfillment
    let action = request.body.result.action; // https://dialogflow.com/docs/actions-and-parameters
    console.log('Actions = '+ JSON.stringify(action));

    let query = request.body.result.resolvedQuery;

    // Parameters are any entites that Dialogflow has extracted from the request.
    const parameters = request.body.result.parameters; // https://dialogflow.com/docs/actions-and-parameters

    // Contexts are objects used to track and store conversation state
    const inputContexts = request.body.result.contexts; // https://dialogflow.com/docs/contexts

    if(action === 'firebase.update'){
        let userId = 'bert.macklin';
        // Check if the user is in our DB
        admin.firestore().collection('users').where('userId', '==', userId).limit(1).get()
            .then(snapshot => {
                let user = snapshot.docs[0]
                if (!user) {
                    // Add the user to DB
                    admin.firestore().collection('users').add({
                        userId: userId
                    }).then(ref => {
                        sendResponse('Added new user');
                    });
                } else {
                    // User in DB
                    sendResponse('User already exists');
                }
            });
    }

    // Function to send correctly formatted responses to Dialogflow which are then sent to the user
    function sendResponse (responseToUser) {
        // if the response is a string send it as a response to the user
        if (typeof responseToUser === 'string') {
            let responseJson = {};
            responseJson.speech = responseToUser; // spoken response
            responseJson.displayText = responseToUser; // displayed response
            response.json(responseJson); // Send response to Dialogflow
        } else {
            // If the response to the user includes rich responses or contexts send them to Dialogflow
            let responseJson = {};

            // If speech or displayText is defined, use it to respond (if one isn't defined use the other's value)
            responseJson.speech = responseToUser.speech || responseToUser.displayText;
            responseJson.displayText = responseToUser.displayText || responseToUser.speech;

            // Optional: add rich messages for integrations (https://dialogflow.com/docs/rich-messages)
            responseJson.data = responseToUser.richResponses;

            // Optional: add contexts (https://dialogflow.com/docs/contexts)
            responseJson.contextOut = responseToUser.outputContexts;

            response.json(responseJson); // Send response to Dialogflow
        }
    }
});

Let us dissect the most important parts in this.

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

These lines load the firebase-functions and firebase-admin modules, and initialize an admin app instance from which Realtime Database changes can be made.

Now, let us look at the cloud function itself.

We extract and store the appropriate variables from the JSON request coming to the function:

    console.log('Request headers: ' + JSON.stringify(request.headers));
    console.log('Request body: ' + JSON.stringify(request.body));

    // An action is a string used to identify what needs to be done in fulfillment
    let action = request.body.result.action; // https://dialogflow.com/docs/actions-and-parameters
    console.log('Actions = '+ JSON.stringify(action));

    let query = request.body.result.resolvedQuery;

    // Parameters are any entites that Dialogflow has extracted from the request.
    const parameters = request.body.result.parameters; // https://dialogflow.com/docs/actions-and-parameters

    // Contexts are objects used to track and store conversation state
    const inputContexts = request.body.result.contexts; // https://dialogflow.com/docs/contexts

Now, we will check to see if the action is ‘firebase.update’ (you will create that action in your agent in a future step) and if it is, we add some data into the Cloud Firestore (or return a message if it already exists).

    if(action === 'firebase.update'){
        let userId = 'bert.macklin';
        // Check if the user is in our DB
        admin.firestore().collection('users').where('userId', '==', userId).limit(1).get()
            .then(snapshot => {
                let user = snapshot.docs[0]
                if (!user) {
                    // Add the user to DB
                    admin.firestore().collection('users').add({
                        userId: userId
                    }).then(ref => {
                        sendResponse('Added new user');
                    });
                } else {
                    // User in DB
                    sendResponse('User already exists');
                }
            });
    }

Explaining the entire code will require that you already understand some of the basic concepts behind writing cloud functions, such as Promises, and anonymous functions. Once again, I suggest watching this playlist. The important thing which is happening here is that we are saving some data to the Cloud Firestore into the “users” collection we created in step 2. (and yes, if you noticed, I am a fan of Parks and Recreation).

This code snippet is not complete. I will leave it as an exercise for the reader to handle multiple actions.

In the next step, we will be deploying this function.

Deploying the function

Once you have modified index.js, you will be deploying it to Cloud Functions.

From the original project directory (FirebaseTestSimple, not functions) type the following into your terminal

firebase deploy --only functions

If this is the first time you are deploying the function, when the function is completely deployed, the function URL will be displayed in the terminal. Future updates to the code and subsequent deployments will not show the function URL. Notice that the URL ends with the function name we used.

Now copy the function URL and paste it into your Dialogflow project’s webhook URL.

We are almost there. The cloud function is now available for you to call from your Dialogflow agent. But there is one more thing to do.

Add a suitable action

You have seen that the cloud function has been deployed successfully. In the code, you might also remember this section

    if(action === 'firebase.update'){
        //let userId = app.getUser().userId;
        let userId = 'bert.macklin';
        // Check if the user is in our DB
        admin.firestore().collection('users').where('userId', '==', userId).limit(1).get()
            .then(snapshot => {
                let user = snapshot.docs[0]
                if (!user) {
                    // Add the user to DB
                    admin.firestore().collection('users').add({
                        userId: userId
                    }).then(ref => {
                        sendResponse('Added new user');
                    });
                } else {
                    // User in DB
                    sendResponse('User already exists');
                }
            });
    }

So we need to create an intent which sets the action to ‘firebase.update’ to see the update happen in our Firebase Cloud Firestore.

I will keep this very simple. The intent definition looks like this:

Please toggle the “Enable webhook for this intent” but don’t turn on the “Use webhook for slot filling”.

In the next step, we will trigger this intent and watch its effect on our data store.

Triggering the data update

Once you create a suitable intent, you are ready to trigger the intent and cause the intent to call the webhook, which is turn will write the data to the Cloud Firestore.

Once you have declared the intent with a suitable action, you can simply type a phrase from your userSays phrase into your Dialogflow console.

Now go to your Firebase console and check to see if the data has been updated:

Does it work as expected?

Congratulations. You have successfully implemented your first update from your Dialogflow agent to your Cloud Firestore.

The steps are quite similar if you simply wish to use the real time database instead of the Cloud Firestore, obviously with some changes to the code that accesses the data.

Troubleshooting

So you tried out the tutorial in its entirety. But you are not getting the expected response.

Here is a troubleshooting sequence.

Use the Show JSON button to see the webhook status

When you don’t see the response you are expecting, click on the Show JSON button and note down what you see in the webhook status.

Click the Show JSON button
You will see error code 200 in the status

Note: In your webhook status, you might see a couple of things.

You might see a message which indicates there was a timeout, or perhaps an Error 500 which is an internal server error.

Checking the Firebase logs

You have noted down the webhook status and the corresponding error. Which type of error is it?

Internal Server Error

If you see a 500 error – an internal server error, then check out your Firebase logs. Here is how to find it:

Go to Firebase console while logged in to your Google account. Click on Functions from the left pane, and click on Logs on the upper tab.

You can see all the logs as you interact with your Cloud Function. Usually, an Internal Server Error will show you an explicit error, e.g. variable is not defined, or perhaps some dependency error. Try to see if you can make sense of the error and then fix it.

Timeout

A more silent and harder to debug failure is a simple timeout. We will resort to the famous printf debugging process in this case. 🙂

Sprinkle your code with plenty of console.log statements, in fact probably one per statement of execution. See how far down your code the console logs show up. When you see an expected console.log message not showing up in your logs, you can usually use it to infer which line of code was throwing the error and proceed with troubleshooting.

What about local debugging?

Firebase cloud functions can also be emulated on your local system using the firebase serve command.

The recently released Zoho SalesIQ v2 allows non-programmers to build chatbots using an easy-to-use code less bot builder. What is really unique about Zoho SalesIQ is the fact that you can also integrate AI into their code less bot builder. In my Zoho SalesIQ chatbots course, I explain how to use Zoho SalesIQ to add a chatbot to your website.

"The magic key I needed as a non-programmer"

The custom payload generator was the magic key I needed (as a non-programmer) to build a good demo with rich responses in DialogFlow Messenger. I've only used it for 30 minutes and am thrilled. I've spent hours trying to figure out some of the intricacies of DialogFlow on my own. Over and over, I kept coming back to Aravind's tutorials available on-line. I trust the other functionalities I learn to use in the app will save me additional time and heartburn.

- Kathleen R
Cofounder, gathrHealth

Similar Posts

13 Comments

  1. Thanks, Aravind. I have also used inline editor to deploy webhook. Sometimes I get deadline exceeded error and all such requests have webhook latency 4990ms. But I have written a very simple code which will not take more than 50ms and most of the time it has webhook latency 20ms.
    What could be the reason for increased latency intermittently?

  2. Hi thanks so much, I have a problem with the webhook, i followed exactly your tutorial but if got this error in firabs functions, log TypeError: Cannot read property ‘action’ of undefined
    at exports.dialogflowFirebaseFulfillment.functions.https.onRequest (/srv/index.js:14:38)
    at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:49:9)
    at /worker/worker.js:783:7
    at /worker/worker.js:766:11
    at _combinedTickCallback (internal/process/next_tick.js:132:7)
    at process._tickDomainCallback (internal/process/next_tick.js:219:9)
    and in dialogflow it says
    Webhook call failed. Error: UNAVAILABLE.
    I used the link as it was given from the console…
    https://us-central1-firebasetest-osmdfo.cloudfunctions.net/dialogflowFirebaseFulfillment
    do i have to use headers?
    Best,
    Alexander

    1. I think this might be a v1/v2 API related thing, this tutorial was written in end of 2017.

      I plan to refresh these tutorials for v2 API, but it will probably be a few weeks before I can get to it.

  3. Hii Aravind. Thankyou! It’s very good your publication.

    In the step: “firebase deploy –only functions” i had a problem with the function and i couldn’t deploy my project to firebase.

    The problem showed for me was:
    C:UsersTiago ToziDocuments3UFVstartupstetbotstestesDatabaseConnectionCourseMalikfunctionsindex.js

    31:9 error Expected catch() or return promise/catch-or-return

    34:17 error Each then() should return a value or throw promise/always-return

    36:21 error Expected catch() or return promise/catch-or-return

    36:21 warning Avoid nesting promises promise/no-nesting

    38:30 error Each then() should return a value or throw promise/always-return

    What i need to do?

      1. @Alexander may you be more explicit please?

        My json apears like this
        {
        “functions”: {
        “predeploy”: [ “npm –prefix “$RESOURCE_DIR” run lint” ]
        }
        }

  4. Hi, I am interested to learn more about using dialogflow with Firebase. Hope you can update this tutorial very soon! I am currently building a FAQs chatbot for my organisation to handle HR queries.