Published on

How to create a Chatbot using Angular + Node.js - Part 1

Chatbot in action!

Figure 1: Chatbot in action!

Have you ever wanted a personal assistant with 24/7 availability, 100% reliability and extraordinary machine learning capabilities? If so, look no further as what you need is a Chatbot for your website/application.

In this tutorial I will show you how to build a Chatbot using Angular, Node/Express.js and Google’s very own Dialogflow!

Overview

This project will involve using Angular, Node.js and Dialogflow from Google in order to construct a Chatbot application. Here are the roles of these technologies as they relate to this project:

  • Dialogflow will be used as the Conversional API
  • Node.js & Express.js will be used to setup the backend server
  • Angular will be used to display the actual Chat Application interface.

This first post will cover how to setup Dialogflow and the backend while the second post will go over the frontend implementation. So without further ado, let’s jump right in!

Prerequisites

In order to build this Chatbot application, you’ll need the following:

  • Angular 8 or higher
  • Node.js 12 or higher
  • A GCP account

Dialogflow Setup

Firstly, I’ll explain why we need to use Dialogflow: We need Dialogflow because it is the actual Conversional API that receives queries and sends responses in the form of HTTP requests to our backend server. Dialogflow is incredibly versitile because it enables us to engage our customers in more natural conversations using Small Talk, it supports 55 different languages and allows us to use it within the Google Cloud Platform ecosystem.

Now, let’s get to the action:

First, go to https://dialogflow.cloud.google.com and click on the Create new agent link on the top left of the screen. Then give your agent a name and click Save like so:

Image 2

Figure 2: Google Dialogflow Console

Now with your agent created, Click on the View all agents link and select a Google Project for your newly created agent. Note: if you don’t have a Google Project for this application then click on Create a new Google Project and follow the instructions to create one. Here’s a screenshot of the agent page:

Image 3

Figure 3: Agent page in Google Dialogflow Console

Once your Dialogflow agent is created, we now need to obtain it’s corresponding Google Project’s API key file. To do this, login into the Google Cloud Platform Console and select your desired Google Project. Now, in the Navigation Menu hover over IAM & Admin then select Service Accounts.

Image 4

Figure 4: IAM and Admin screen in Google Cloud Platform

On the following screen, if you already have a service account for this Google Project then select Add Key -> Create New Key. Then in the following modal, select 'JSON' and click Create to download the API Key file to your local machine.

Once downloaded, save it to a secure place in your coding workspace(if you’re moving in to the project folder, make sure to include it’s name in the .gitignore file for that project, as we do not want this credentials file to be uploaded to remote source control!).

After that, you need to set an environment variable for our backend application to actually know where the API key file is located. The syntax will depend on your operating system so if you’re using:

  • Windows: run the command: set GOOGLE_APPLICATION_CREDENTIALS=<path_to_your_API_key_file> on Command Prompt
  • MacOSX or Linux: run the command: export GOOGLE_APPLICATION_CREDENTIALS=<path_to_your_API_key_file> on Terminal
Image 5

Figure 5: Setting the Google Application Credential env variable to the API key file location

Finally, to give permission to use the Dialogflow API we need to give the permission level of Owner to the member assigned for our Google Project. To do this, login to the Google Cloud Platform, open up the Navigation Menu, select IAM & Admin and then IAM. In the following screen, find the matching member for your Google Project linked to Dialogflow and click the pencil icon. Then, make sure to select the role of Owner and click Save in the following screen, like so:

Image 6

Figure 6: Configuring the IAM User in Google Cloud Platform

And that will conclude our Dialogflow setup!

Backend Setup

On to the backend application!

Create a folder to hold the backend application, then cd into it and run npm init to create a package.json file.

Now, you’ll have install the following dependencies by running this long command:

npm install --save @google-cloud/dialogflow@4.1.0 @okta/jwt-verifier@2.1.0 @types/cors@2.8.10 @types/express@4.17.11 body-parser@1.19.0 cors@2.8.5 errorhandler@1.5.1 express@4.17.1 express-bearer-token@2.4.0 express-session@1.17.2 http@0.0.1-security method-override@3.0.0 methods@1.1.2 morgan@1.10.0 nodemon@2.0.7 path@0.12.7 sqlite3@5.0.2 tsc@1.20150623.0 typeorm@0.2.32 typescript@4.2.4 uuid@8.3.2

to install all the required dependencies needed for our Node/Express.js backend

After installing those dependencies we’ll need to add the following content to our entry point file(which is index.js by default):

// ./{root_folder}/index.js
var express = require('express'),
    bodyParser = require('body-parser'),
    session = require('express-session'),
    cors = require('cors'),
    errorhandler = require('errorhandler'),
    dialogflowIndex = require("./routes/api");
    index = require('./routes/index');

var isProduction = process.env.NODE_ENV === 'production';

// Create global app object
var app = express();

app.use(cors());

// Normal express config defaults
app.use(require('morgan')('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use(require('method-override')());
app.use(express.static(__dirname + '/public'));

app.use(session({ secret: 'conduit', cookie: { maxAge: 60000 }, resave: false, saveUninitialized: false  }));

if (!isProduction) {
  app.use(errorhandler());
}

app.use("/api", dialogflowIndex);

/// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.json({'errors': {
    message: err.message,
    error: {}
  }});
});

// finally, let's start our server...
var server = app.listen( process.env.PORT || 3000, function(){
  console.log('Listening on port ' + server.address().port);
});

The index.js file is primarily responsible for setting up the routes for the application and starting the application server. It imports the ./routes/api/index.js file, which will hold the controller to send & receive HTTP requests from Dialogflow, so let’s create that file now:

// ./{root_folder}/routes/api/index.js
var router = require('express').Router();
var runIntent = require("./quickstart").runIntent;

router.post("/requestText", function(req, res){
    // var intentRequest = createSessionPath(req.params.projectId);
    // console.log(req.params.projectId);
    (async() => {
        var result = await runIntent(req.body.projectId, req.body.requestText);
        return res.send(
            {
                "responseMessage": result.Response,
                "originalQuery": result.Query,
                "intent": result.intent
            });
    })()
});
module.exports = router;

As you see, the /routes/api/index.js file contains a POST request using the createSessionPath() method contained in the ./routes/api/dialogflow.js file.

As we have not yet created ./routes/api/dialogflow.js file yet, let us do so now:

// ./{root_folder}/routes/
const dialogflow = require('@google-cloud/dialogflow');
const uuid = require('uuid');

const sessionId = uuid.v4();
/**
 * Send a query to the dialogflow agent, and return the query result.
 * @param {string} projectId The project to be used
 */
async function runIntent(projectId, requestText) {
// A unique identifier for the given session
const sessionClient = new dialogflow.SessionsClient();

const sessionPath = sessionClient.projectAgentSessionPath(
    projectId,
    sessionId
);
const intentRequest = {
    session: sessionPath,
    queryInput: {
    text: {
        // The query to send to the dialogflow agent
        text: requestText,
        // The language used by the client (en-US)
        languageCode: 'en-US',
    },
    },
};

// The text query request.
// Send request and log result
const responses = await sessionClient.detectIntent(intentRequest);
const result = responses[0].queryResult;

return await {
        "Query": result.queryText,
        "Response": result.fulfillmentText,
        "Intent": result.intent.displayName
    };
}

module.exports.runIntent = runIntent;

The file above basically uses the @google-cloud/dialogflow library to send an API request to Dialogflow while using the API key file, whose location is revealed via the GOOGLE_APPLICATION_CREDENTIALS environment variable we set earlier, as authentication. Once the response is received, an object containing the response text from Dialogflow(held in the Response field), the original query(held in the Query field) and the intent(set in the Dialogflow Console page) is sent back to the caller, which in this case is the POST route in /routes/api/index.js.

Now that the route /api/requestText is setup in the backend, we can start the backend server. Since we are currently in a development environment, we can use nodemon to take care of automatically restarting the application whenever change is made. To use nodemon we’ll need to add the following code to our package.json in the backend:

  "scripts": {
    "start": "nodemon index.js"
  },

Finally, we can start our Node/Express.js application by running: npm start

Backend server running successfully!

Figure 7: Backend server running successfully!

And with that, we have finished our backend application setup!

Conclusion

Well, that’s it for Part 1. Stay tuned for Part 2 of this series where I teach you how to implement a front-end application so that our Chatbot application is fully functional.

I hope you found this article helpful. Thanks so much for reading my article! Feel free to follow me on Twitter and GitHub, connect with me on LinkedIn and subscribe to my YouTube channel.