How to upgrade from Api.ai to Dialogflow v2 using Firebase Cloud Functions

Published: Tuesday, Aug 3, 2021

TL;DR

You can add Dialogflow v2 chatbot integration to a website for free using Firebase Cloud Functions, but there are a few tricks you need to know about. Read the details below or just check out the code.

Details

DialogFlow logo

Four years ago, I thought it would be fun to add a chatbot to my website. I had been playing with Chatbot integration at work, and I realized it could be a fun way to add some interactivity to this personal site.

Back then, Dialogflow was called Api.ai and it offered a really easy way to set up a chatbot agent and integrate it into a website via simple REST calls. They even provided a client-side JS library to help. So I entered some info about me to the chatbot agent, added a nice UI on top of the integration (thanks to Botui), slapped it all together on my website and promptly moved on to other things.

Over the years, Google bought Api.ai, they renamed it to Dialogflow, they deprecated a lot of Api.ai code, and most recently they shut down the client-side library altogether. That meant that suddenly my blog’s chatbot integration wasn’t working anymore! 😱

Logo

I assumed there would be a drop-in replacement with the new API, dubbed Dialogflow v2. But it turns out that Google has adopted a new authentication approach which precludes a simple client-side integration. So fixing this was going to be harder than I thought. Dialogflow now only offers a server-side Node.js library for integration purposes (in addition to other server-side language integrations like C++ and Java). So how was I going to integrate my static, 100% client-side website with Dialogflow?

After much pondering, I realized that Firebase Cloud Functions could be the perfect intermediary between Dialogflow and my website.

Read on to see how I migrated Api.ai to Dialogflow with the help of Cloud Functions.

But how to get Diagflow to work in a Cloud Function? 🤔

Getting started is easy. To use the Dialogflow Node.js library inside a Firebase Cloud Function, you just need to add the following to your code:

const dialogflow = require('@google-cloud/dialogflow');

Then, to query your Dialogflow agent, you just need to prepare a request and call Dialogflow as follows (see example):

  const sessionClient = new dialogflow.SessionsClient();
  ...
  const responses = await sessionClient.detectIntent(request);

This almost works, but not quite. When I first tried this, I got a strange error messaging saying Dialogflow API has not been used in project firebase-cli before or it is disabled. Some poking around the DialogFlow Node.js Library Github clarified that this error message is misleading and really means that you haven’t properly authenticated your request with Dialogflow.

In order to authenticate, the Dialogflow documentation recommends that you create a service account and a new key, download the key as JSON, and then set the path to this service account JSON file as an environment variable like export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH". Unfortunately this won’t work in a Cloud Function because there’s no way to specify a whole file as an environment variable. Firebase only allows key/value pairs to be set as environment variables.

Some more research led to a better, albeit not well documentated, approach. It turns out that the Dialogflow client constructors take an options argument which can contain authentication options. One of these authentication options is the keyFilename option which lets you specify a service account key JSON file.

This means, instead of creating the sessionClient as shown in the example, you should instead instantiate the Dialogflow client as follows:

const sessionClient = new dialogflow.SessionsClient({
	keyFilename: './service-account.json'
});

Assuming you followed the instructions to create a service account and downloaded the JSON file to service-account.json, then your function will now authenticate correctly with Dialogflow and it connect to your agent as expected!

You can see an example of all of this working together here.