Back to search

Building a chat bot with Nest.js and Dialogflow

  • Christian Nwamba
May 13th, 2018
You will need Node and npm installed on your machine. A basic understanding of TypeScript and Node will be helpful.

Introduction

Over the past few years, advances in machine learning, especially natural language processing (NLP), has made it easy for computers to analyze and derive meaning from human language in a smart way. With this, developers can now create smaller, simpler and more intuitive natural language processing software.

In this tutorial, I will be showing you how to build a basic chat bot using the Dialogflow conversation platform formerly know as api.ai by Google. This bot will engage in a simple conversation with a user and respond to text from an input field.

https://www.youtube.com/watch?v=GKGu0hfeNlU&

Prerequisites

A basic understanding of TypeScript and Node.js will help you get the best out of this tutorial. I assume that you already have Node and npm installed, if otherwise quickly check Node.js and npm for further instructions and installation steps.

Finally, here is a quick overview of the technologies that we will be using in this post.

  • Nest.js: a progressive framework for building efficient and scalable server-side applications; built to take advantage of modern JavaScript but still preserving compatibility with pure JavaScript.
  • Dialogflow: an engine for building conversational experiences. It leverages Google’s machine learning and natural language processing technology, thereby giving developers the ability to utilize a user friendly interface to build chat bots and deploy them to any platform.

Setting up the project

Installing the Nest.js starter project

First, you need to install the Nest.js starter project on GitHub using Git. To do this, let’s run a command that will clone the starter repository into a new project folder named nest-bot-pusher on your machine. Open your terminal or command prompt and run the command below:

    $ git clone https://github.com/nestjs/typescript-starter.git nest-bot-pusher

Go ahead and change directory into the newly created folder and install all the dependencies for the project.

    // change directory
    cd nest-bot-pusher

    // install dependencies
    npm install

Running the application

Start the application with :

    npm start

The command above will start the application on the default port used by Nest.js. Open your browser and navigate to http://localhost:3000. You should see a page with a welcome message.

nest-hello-world

Installing server dependencies

Run the command below to install the server dependencies required for this project.

    npm install ejs body-parser pusher axios --save
  • ejs: this is a simple templating language for generating HTML markup with plain JavaScript.
  • Body-parser: a middleware used for extracting the entire body portion of an incoming request stream and exposing it on req.body.
  • Pusher: a Node.js client to interact with the Pusher REST API
  • Axios: a Promise based HTTP client for the browser and Node.js.

Building a Dialogflow agent

Now that we have our Nest.js application set up, let’s configure our chatbot agent in Dialogflow. Head over to their official website and create a free account.

chat-bot-nest-dialogflow-signup

Create an agent

A Dialogflow agent is essentially the chat bot and its most fundamental purpose is to detect the intent that comes from a user and respond appropriately. For instance, if a user sends a message such as “Who are you?”, if the agent has been trained to recognize such a phrase, it will detect the intent and respond to the user based on a configured response.

Once you are done setting up an account, click on the Create Agent button to create an agent:

chat-bot-nest-dialogflow-welcome

Give you agent a name that suits your purpose and finally click on the CREATE button to complete the process

chat-bot-nest-dialogflow-create-bot

Creating small talk

For a quick start, we can easily program our chat bot for common chat style questions in the small talk panel. This will give our bot a basic level of interactivity with the users. By default, there are responses with predefined phrases within the small talk panel. Go ahead and customize the response as you deem fit. For now, we are going to keep it simple and respond to few questions:

💡 Note: ensure that you enable the small talk indicated by the red box in the image below:

chat-bot-nest-dialogflow-enable-smalltalk

You can now use the test console at the right hand side corner to try out a sentence. As in the image below:

chat-bot-nest-dialogflow-test-console

We’ve now set up the chat agent with the name Nest-bot-chat. Let’s put this to use in our application.

Pusher account setup

We will depend on Pusher to provide a realtime update for our application. So, head over to their website and sign up for a free account.

nest-phone-pusher-1

Create a new app by selecting Channels apps on the sidebar and clicking Create Channels app button on the bottom of the sidebar:

nest-phone-pusher-2

Configure an app by providing basic information requested in the form presented. You can also choose the environment you intend to integrate Pusher with, which will provide you with boilerplate code to get you started.

nest-phone-pusher-3

You can retrieve your keys from the App Keys tab:

nest-phone-pusher-4

Integrating the chatbot agent into our application

We’ll be making an API call to Dialogflow API to develop a conversational interface for our application. For this, we will need a developer access token generated for our newly created agent from the dashboard. Copy the developer access token and save it as we will need to use it for the API call.

chat-bot-nest-dialogflow-dev-token

Bootstrap the application

Nest.js uses the Express library and therefore, favors the popular MVC pattern.

To set this up, open up the main.ts file and update it with the content below:

    // ./src/main.ts

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';

    import * as bodyParser from 'body-parser';
    import * as express from 'express';
    import * as path from 'path';

    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      app.use(express.static(path.join(__dirname, 'public')));
       app.set('views', __dirname + '/views');
       // set ejs as the view engine
       app.set('view engine', 'ejs');
      await app.listen(3000);
    }
    bootstrap();

Setting up the homepage

The views folder will contain all templates for this application. Go ahead and create it within the src folder. Once you are done, create a new file named index.ejs right inside the newly created views folder and update the content with:

    // ./src/views/index.ejs

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
        <link rel="stylesheet" href="/style.css">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

        <title> Nest Chat Bot </title>
    </head>
    <body>
        <div id="app">

            <div class="pageWrapper">

                <div class="row">
                    <div class=" chat-wrapper">
                        <div v-for="chat in chats" class="chat-holder">
                            <div class="active-user message message-to">
                                <span> {{ chat.query }} </span>
                                <img src="/images/user.png" class="avatar" alt="Active user avatar">
                            </div>

                            <div class="bot message message-from">
                                <img src="/images/bot.png" class="avatar" alt="Bot avatar">
                                <span> {{ chat.speech }} </span>
                            </div>

                        </div>

                    </div>

                    <div class="form-group box">
                        <input type="text" placeholder="Enter your message and press enter!" @keyup.enter="sendChat" class="form-control">
                    </div>
                </div>
            </div>
        </div>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.js"></script>
        <script src="https://js.pusher.com/4.1/pusher.min.js"></script>
        <script src="/main.js"></script>
    </body>
    </html>

Here, we are simply building a basic layout for the interactive interface for the chat bot.

Firstly, we included a link to the Bootstrap CDN file to add some default styling and layout to our application. We also added a custom stylesheet for further styling. We will create this stylesheet later in this tutorial. Also included in a <script> tag just before the page title is a CDN file for Vue.js. This is to ensure that Vue.js is loaded immediately the index.ejs file is rendered.

Furthermore, on this page, we used an event handler to listen to keyboard events using key modifier aliases made available by Vue.js. This process will be discussed later in the tutorial.

Finally, we included a CDN file each for Axios and Pusher and then proceeded to add a custom script file named main.js. To set up this file, go ahead and create a public folder within the src folder in the application and create the main.js file within it.

Update the controller

When a user interacts with our the chat bot from our application, the Dialogflow API sends a query request to Dialogflow and returns structured data in JSON format with an action and parameters. Let’s configure our application logic.

The Nest.js starter project comes installed with a default controller named app.controller.ts. Open this file and update it with the code below:

    //  ./src/app.controller.ts

    import { BotService } from './bot/bot.service';
    import { Get, Post, Controller, Res, HttpStatus, Body } from '@nestjs/common';
    @Controller()
    export class AppController {
      constructor(private botService:BotService){}

      @Get()
      dialogueHomepage(@Res() res) {
          res.render('index');
      }
      @Post()
      startDialogue(@Res() res, @Body() data) {
          this.botService.sendDialogue(data);
          res.status(HttpStatus.OK).send("Posted successfully");
      }
    }

This controller will ensure that Nest.js maps every / route to the index.ejs file.

Initialize the chat room and post a message

To initialize the chat room, we will start by creating a new Vue instance with the Vue function. Open up the main.js file created earlier and paste the code below in it:

    // ./src/public/main.js

    new Vue({
      el: '#app',
      data: {
        chats: [],
      },
      methods: {

        sendChat(event) {
          const chatMessage = event.target.value;

          if(event.keyCode === 13 && !event.shiftKey) {
            const chat = {
              message: chatMessage
            };

            event.target.value = "";

            axios.post('/', chat)
            .then( data => {
              console.log(data);
            });
          }
        }
      }
    })

Here, we created a Vue instance and as part of the options, we bound Vue to a div with an id of app and also passed data options. Further down, we used a method sendChat() to retrieve the message from a user and finally use Axios to send it to the server.

Configure the bot service

Our AppController depends on a service named BotService to send the message from a user to the Dialogflow API. Let’s create this service. Create a bot folder within the src folder and proceed to create a new file named bot.service.ts within it. Next, open the newly created file and paste the code below into it:

    // ./src/bot/bot.service.ts

    import { Component } from '@nestjs/common';
    import axios from 'axios';

    @Component()
    export class BotService {

        private baseURL: string = "https://api.dialogflow.com/v1/query?v=20150910";
        private token: string = "YOUR_DEVELOPER_ACCESS_TOKEN";

        sendDialogue(info) {

            let data = {
                query : info.message,
                lang: 'en',
                sessionId: '123456789!@#$%'
            }
            axios.post(`${this.baseURL}`, data, {headers: { Authorization: `Bearer ${this.token}` }})
            .then( response => {
                this.postToPusher(response.data.result.fulfillment.speech, data.query);
            })    
        }
    }

Here, we created two private properties that can be accessed within our service:

  1. baseURL: The URL for interacting with Dialogflow API
  2. token: This is the developer access token obtained from our dashboard earlier. Make sure you replace the YOUR_DEVELOPER_ACCESS_TOKEN with the appropriate credential from your dashboard

In addition, we created a new data object containing the message posted by user, default language, and a unique sessionId (can be up to 36 symbols long). We then proceeded to use Axios to make an API call to Dialogflow API, passing the data object and an authorization header with the token

Finally, we used a custom method named postToPusher() after the response was returned from Dialogflow API. We’ll create the method in the next section.

Post to pusher

The postToPusher() method takes in two values as an argument and passes it down to Pusher:

    // ./src/bot/bot.service.ts

    ...
    @Component()
    export class BotService {
            ...

        sendDialogue(info) {
                ... 
        }

        postToPusher(speech, query) {
            const Pusher = require('pusher');

            var pusher = new Pusher({
                appId: 'YOUR_APP_ID',
                key: 'YOUR_API_KEY',
                secret: 'YOUR_SECRET_KEY',
                cluster: 'CLUSTER',
                encrypted: true
            });
            const response = {
                query: query,
                speech: speech
            }

            pusher.trigger('bot', 'bot-response', response);
        }

    }

We initialized Pusher with the required credentials so as to trigger an event named bot-response through a bot channel. Don’t forget to replace YOUR_APP_ID, YOUR_API_KEY, YOUR_SECRET_KEY and CLUSTER with the right credentials obtained from your dashboard.

Register the bot service

At the moment, our application does not recognize the newly created service. Let’s change this by editing our module file 'app.module.ts'. Put the service into the 'components' array of the '@Module() decorator.

    // ./src/app.module.ts

    import { BotService } from './bot/bot.service';
    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    @Module({
      imports: [],
      controllers: [AppController],
      components: [BotService],
    })
    export class AppModule {}

Displaying chat messages

In order to receive the chat messages in realtime, we’ll use the subscribe() method from Pusher to subscribe to the created bot channel. Update the main.js file with the code below and remember to add your Pusher keys.

    // ./src/public/main.js

    new Vue({
      el: '#app',
      data: {
        chats: [],
      },
      created() {
        let pusher = new Pusher('YOUR_API_KEY', {
          cluster: 'CLUSTER',
          encrypted: true
        });

        const channel = pusher.subscribe('bot');
        channel.bind('bot-response', data => {
          const response = {
            speech: data.speech,
            query: data.query
          }
          this.chats.push(response);
        });
      },
      methods: {
      ...
      }
    })

Stylesheet

Before, we will run the application, let’s us set up the custom stylesheet included on our index.ejs file earlier. Locate the public folder and create a new file named style.css within it. Next, open the file and paste the code below:

    // ./src/public/style.css

    .pageWrapper {
      width: 500px;
      margin: 0 auto;
      padding-top: 60px;
      overflow: scroll;
    }
    .box {
      position: fixed;
      bottom: 0;
      width: 510px;
      padding: 5px 10px;
      margin-top: 30px;
    }
    .box input {
    width: 100% !important;
    height: 80px;
    }
    .chat-holder {
      max-width: 400px;
      box-sizing: border-box;
    }
    .chat-wrapper {
      margin-bottom: 20px;
      height: 500px;
      overflow: scroll;
    }
    .avatar{
      width: 30px;
      margin-right: 20px;
    }
    .message {
      border-radius: 50px;
      margin: 0 15px 10px;
      padding: 5px 10px;
      position: relative;
      font-weight: bold;
      width: 400px;
    }
    .message-to {
      background-color: #2095FE;
      color: #fff;
      margin-left: 100px;
      text-align: right;
    }
    .message-from {
      background-color: #E5E4E9;
      color: #363636;
      margin-right: 100px;
    }

You can download the images used for this demo here on GitHub. Once you are done, create a new folder named images within the public folder and save them there.

Test the application

Restart the development server if it is currently running. Check your page on [http://localhost:3000](http://localhost:3000.)

chat-bot-nest-dialogflow-screenshot

Conclusion

Here, we have been able to build a chat bot with a predefined response to chat from other users. The intention was to give you a general building block that can be built on, explored, and improved to build an amazing chat bot that can do much more.

I hope this tutorial was helpful and gave you enough information required to start building bots tailored for other use cases, as you deem fit in your organization.

The source code for this tutorial can be found here on GitHub. Feel free to explore and add more features.

  • Channels

© 2018 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered office is at 28 Scrutton Street, London EC2A 4RP.