Build a live comments feed with Go and Vue.js
Tags: pusher, vue, go
Published: 2018-11-19 17:10:45

---
title: Build a live comments feed with Go and Vue.js
published: true
description: 
tags: pusher,vue,golang
---
> You will need Go, and SQLite installed on your machine. Basic knowledge of Go and JavaScript will be helpful.

The internet is a breeding ground for all kinds of social activities because it expands the possibilities of communication. In order to keep web applications social and enjoyable, it is important for them to have one or more interfaces for the users to interact through. One such interface is the comment section.

The comment section is where users can discuss a subject (post, video, picture) that they have access to. In the past, for a user to see a comment from another user, the user would have to refresh the browser window. However, with realtime comments now we can automatically pull in comments live. This article will cover how we can build realtime commenting using Pusher.

By the end of this article, we will have built an application that looks like this:

![](//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif)

## Requirements

To follow along with this article, you will need the following:

- Go (version >= 0.10.x) installed on your computer. Heres how you can [install Go](https://golang.org/doc/install).
- SQLite (v3.x) installed on your machine. [Installation guide](http://www.sqlitetutorial.net/download-install-sqlite/).
- Basic knowledge of the Go programming language.
- Basic knowledge of JavaScript (ES6).
- Basic knowledge of Vue.js.

## Getting a Pusher Channels application

The first step will be to get a Pusher Channels application. We will need the application credentials for our realtime features to work.

Go to the Pusher website and create an account. After creating an account, you should create a new application. Follow the application creation wizard and then you should be given your application credentials, we will use this later in the article.

![](//images.ctfassets.net/1es3ne0caaid/2h3e4hp58cIW6acMc8Mgy0/dfeac8f3cdffb41174e93ad567233cd0/go-comments-app-keys.png)

Now that we have our application, let’s move on to the next step.

## Setting up the codebase

Let’s start by navigating into the `src` directory located in the `$GOPATH`. Then we’ll create a new directory for our app there.

```
    $ cd $GOPATH/src
    $ mkdir go-realtime-comments
    $ cd go-realtime-comments

```

Create a `comments.go` file in this directory.

Before we write code, we need to import a few Go packages that will help run our projects. We will install the [Echo framework](https://echo.labstack.com) and the [SQLite packages](https://github.com/mattn/go-sqlite3). Run the following commands to pull in the packages:

```
    $ go get github.com/labstack/echo
    $ go get github.com/labstack/echo/middleware
    $ go get github.com/mattn/go-sqlite3

```

> ⚠️ If you use Windows and you encounter the error ‘cc.exe: sorry, unimplemented: 64-bit mode not compiled in ‘, then you need a Windows gcc port, such as <https://sourceforge.net/projects/mingw-w64/>. Also see this [GitHub issue](https://github.com/mattn/go-sqlite3/issues/297).

With your favorite editor, open the `comments.go` file and paste in the following lines of code:

```
    <span class="hljs-keyword">package</span> main

    <span class="hljs-keyword">import</span> (
        <span class="hljs-comment">// "database/sql"</span>

        <span class="hljs-string">"github.com/labstack/echo"</span>
        <span class="hljs-string">"github.com/labstack/echo/middleware"</span>
        <span class="hljs-comment">// _ "github.com/mattn/go-sqlite3"</span>
    )
```

## Configuring the database and routes

Every Go application must have a `main` function. This is where the execution of the application will start from, so let’s create our `main` function:

In the `comments.go` file, add the following below the imports:

```
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {

        <span class="hljs-comment">// Echo instance</span>
        e := echo.New()

        <span class="hljs-comment">// Middleware</span>
        e.Use(middleware.Logger())
        e.Use(middleware.Recover())

        <span class="hljs-comment">// Define the HTTP routes</span>
        e.GET(<span class="hljs-string">"/comments"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
            <span class="hljs-keyword">return</span> c.JSON(<span class="hljs-number">200</span>, <span class="hljs-string">"GET Comments"</span>)
        })

        e.POST(<span class="hljs-string">"/comment"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
            <span class="hljs-keyword">return</span> c.JSON(<span class="hljs-number">200</span>, <span class="hljs-string">"POST a new Comment"</span>)
        })

        <span class="hljs-comment">// Start server</span>
        e.Logger.Fatal(e.Start(<span class="hljs-string">":9000"</span>))
    }
```

In the main function, we have defined some basic route handler functions, these functions basically return hard coded text to browser on request. The last line will start Go’s standard HTTP server using Echo’s start method and listen for requests port 9000.

We can test that the application works at this stage by running it and making some requests using [Postman](https://www.getpostman.com/).

Here is how you can run the application:

```
    $ go run ./comments.go

```

We can send HTTP requests using Postman. Here’s a sample GET request using Postman:

![](//images.ctfassets.net/1es3ne0caaid/1hOAHfzR8C2SocUciKSaEi/de4455b61abfaed1da7f612a6b1c75f6/go-comments-get-example.png)

POST request with Postman:

![](//images.ctfassets.net/1es3ne0caaid/1uCCDXGXBeus4mSKgSKYoi/a66e13b4921d54b799a3ceae40c2b4d1/go-comments-post-example.png)

We will create a function that will initialize a database and for that we need the SQL and SQLite3 drivers. We already added them to the `import` statement so uncomment them. We will also create a function that will migrate the database using a database schema defined inside the function.

Open the `comments.go` file and paste the following code before the `main` function:

```
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initDB</span><span class="hljs-params">(filepath <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">sql</span>.<span class="hljs-title">DB</span></span> {
        db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, filepath)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }

        <span class="hljs-keyword">if</span> db == <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(<span class="hljs-string">"db nil"</span>)
        }
        <span class="hljs-keyword">return</span> db
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">migrate</span><span class="hljs-params">(db *sql.DB)</span></span> {
        sql := <span class="hljs-string">`
        CREATE TABLE IF NOT EXISTS comments(
                id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                name VARCHAR NOT NULL,
                email VARCHAR NOT NULL,
                comment VARCHAR NOT NULL
        );
       `</span>
        _, err := db.Exec(sql)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }
    }
```

Next add the following code to the top of the `main` function:

```
    <span class="hljs-comment">// [...]</span>

    <span class="hljs-comment">// Initialize the database</span>
    db := initDB(<span class="hljs-string">"storage.db"</span>)
    migrate(db)

    <span class="hljs-comment">// [...]</span>
```

We can now check that these functions are being called and the database is created during execution by running the application:

```
    go run comments.go

```

> ⚠️ If you were already running the Go application you would need to kill the process using ctrl+c on your keyboard and then restart it to see changes.

When the application is run for the first time, a `storage.db` file will be created in the working directory if it did not previously exist.

## Setting up the handlers

We have tested that our application listens on the specified port 9000 and handles the HTTP requests as we configured it to. However, the current handler functions simply return hard-coded text to the browser so let’s create new handler functions to handle responses to the routes.

Create a new folder in the root directory named `handlers`:

```
    $ mkdir handlers
    $ cd handlers

```

Next create a `handlers.go` file and paste the following:

```
    <span class="hljs-keyword">package</span> handlers

    <span class="hljs-keyword">import</span> (
        <span class="hljs-string">"database/sql"</span>
        <span class="hljs-string">"go-realtime-comments/models"</span>
        <span class="hljs-string">"net/http"</span>
        <span class="hljs-string">"github.com/labstack/echo"</span>
    )
```

Now we need to go back to the `comments.go` file and import the handlers package:

```
    import (
        "go-realtime-comments/handlers"

        // [...]
    )

```

In the same file, replace the route definitions from earlier with the ones below:

```
    <span class="hljs-comment">// [...]</span>

    <span class="hljs-comment">// Define the HTTP routes</span>
    e.File(<span class="hljs-string">"/"</span>, <span class="hljs-string">"public/index.html"</span>)
    e.GET(<span class="hljs-string">"/comments"</span>, handlers.GetComments(db))
    e.POST(<span class="hljs-string">"/comment"</span>, handlers.PushComment(db))

    <span class="hljs-comment">// [...]</span>
```

Next paste the following code in the `handlers.go` file below the import statement:

```
    <span class="hljs-keyword">type</span> H <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">interface</span>{}

    <span class="hljs-comment">//GetComments handles the HTTP request that hits the /comments endpoint</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GetComments</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">echo</span>.<span class="hljs-title">HandlerFunc</span></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
            <span class="hljs-keyword">return</span> c.JSON(http.StatusOK, models.GetComments(db))
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">echo</span>.<span class="hljs-title">HandlerFunc</span></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
            <span class="hljs-keyword">var</span> comment models.Comment

            c.Bind(&comment)

            id, err := models.PushComment(db, comment.Name, comment.Email, comment.Comment)
            <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span> c.JSON(http.StatusCreated, H{
                    <span class="hljs-string">"created"</span>: id,
                })
            }

            <span class="hljs-keyword">return</span> err
        }
    }
```

The `GetComments` function fetches and returns comments from the database while the `PushComment` saves comments to the database and returns a response.

## Setting up the models

To create the model package, we need to create a new folder in the root directory of our application:

```
    $ mkdir models
    $ cd models

```

Next create a `models.go` file and paste the following code:

```
    <span class="hljs-keyword">package</span> models

    <span class="hljs-keyword">import</span> (
        <span class="hljs-string">"database/sql"</span>
        _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
    )
```

Let’s create a Comment `type`, which is a struct with four fields:

- `ID` - the ID of the comment.
- `Name` - the username of the user who made the comment.
- `Email` - the email of the user who made the comment.
- `Comment` - the comment.

In Go, we can add metadata to variables by putting them within backticks. We can use this to define what each field should look like when converted to `JSON`. This will also help the `c.Bind` function know how to map `JSON` data when registering a new comment.

Let’s define the structs for `Comment` and `CommentCollection`. In the `models.go` file paste in the following below the imports:

```
    <span class="hljs-keyword">type</span> Comment <span class="hljs-keyword">struct</span> {
        ID      <span class="hljs-keyword">int</span>    <span class="hljs-string">`json:"id"`</span>
        Name    <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
        Email   <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"email"`</span>
        Comment <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"comment"`</span>
    }

    <span class="hljs-keyword">type</span> CommentCollection <span class="hljs-keyword">struct</span> {
        Comments []Comment <span class="hljs-string">`json:"items"`</span>
    }
```

Next, paste in the following code after the structs:

```
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GetComments</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">CommentCollection</span></span> {
        sql := <span class="hljs-string">"SELECT * FROM comments"</span>
        rows, err := db.Query(sql)

        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }

        <span class="hljs-keyword">defer</span> rows.Close()

        result := CommentCollection{}

        <span class="hljs-keyword">for</span> rows.Next() {

            comment := Comment{}
            err2 := rows.Scan(&comment.ID, &comment.Name, &comment.Email, &comment.Comment)
            <span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
                <span class="hljs-built_in">panic</span>(err2)
            }

            result.Comments = <span class="hljs-built_in">append</span>(result.Comments, comment)
        }

        <span class="hljs-keyword">return</span> result
    }
```

The `GetComments` function is responsible for retrieving all the available comments from the database and returning them as an instance of the `CommentCollection` that we defined.

Next, paste in the following code below the one above:

```
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB, name <span class="hljs-keyword">string</span>, email <span class="hljs-keyword">string</span>, comment <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int64</span>, error)</span></span> {
        sql := <span class="hljs-string">"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"</span>
        stmt, err := db.Prepare(sql)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }

        <span class="hljs-keyword">defer</span> stmt.Close()

        result, err2 := stmt.Exec(name, email, comment)
        <span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err2)
        }

        <span class="hljs-keyword">return</span> result.LastInsertId()
    }
```

The `PushComments` function adds a new comment to the database.

## Building the frontend

Next, create a `public` folder in our application’s root directory and create an `index.html` file inside it.

Open the `index.html` file and paste in this code:

```
    <span class="hljs-meta"></span>
    <span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">head</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"ie=edge"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">title</span>></span>Realtime comments<span class="hljs-tag"></<span class="hljs-name">title</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
          @<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">48em</span>) {
            <span class="hljs-selector-tag">html</span> {
              <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
            }
          }
          <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">font-family</span>: Georgia, <span class="hljs-string">"Times New Roman"</span>, Times, serif;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#555</span>;
          }
          <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-class">.h1</span>, <span class="hljs-selector-tag">h2</span>, <span class="hljs-selector-class">.h2</span>, <span class="hljs-selector-tag">h3</span>, <span class="hljs-selector-class">.h3</span>, <span class="hljs-selector-tag">h4</span>, <span class="hljs-selector-class">.h4</span>, <span class="hljs-selector-tag">h5</span>, <span class="hljs-selector-class">.h5</span>, <span class="hljs-selector-tag">h6</span>, <span class="hljs-selector-class">.h6</span> {
            <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Helvetica Neue"</span>, Helvetica, Arial, sans-serif;
            <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
          }
          <span class="hljs-selector-class">.blog-masthead</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
            <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#428bca</span>;
            <span class="hljs-attribute">box-shadow</span>: inset <span class="hljs-number">0</span> -.<span class="hljs-number">1rem</span> .<span class="hljs-number">25rem</span> <span class="hljs-built_in">rgba</span>(0,0,0,.1);
          }
          <span class="hljs-selector-class">.nav-link</span> {
            <span class="hljs-attribute">position</span>: relative;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
            <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#cdddeb</span>;
          }
          <span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-pseudo">:focus</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
            <span class="hljs-attribute">background-color</span>: transparent;
          }
          <span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-class">.active</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
          }
          <span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-class">.active</span><span class="hljs-selector-pseudo">::after</span> {
            <span class="hljs-attribute">position</span>: absolute;
            <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">height</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">margin-left</span>: -.<span class="hljs-number">3rem</span>;
            <span class="hljs-attribute">vertical-align</span>: middle;
            <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
            <span class="hljs-attribute">border-right</span>: .<span class="hljs-number">3rem</span> solid transparent;
            <span class="hljs-attribute">border-bottom</span>: .<span class="hljs-number">3rem</span> solid;
            <span class="hljs-attribute">border-left</span>: .<span class="hljs-number">3rem</span> solid transparent;
          }
          @<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">40em</span>) {
            <span class="hljs-selector-class">.blog-title</span> {
              <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3.5rem</span>;
            }
          }
          <span class="hljs-selector-class">.sidebar-module</span> {
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
          }
          <span class="hljs-selector-class">.sidebar-module-inset</span> {
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
            <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f5f5f5</span>;
            <span class="hljs-attribute">border-radius</span>: .<span class="hljs-number">25rem</span>;
          }
          <span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">p</span><span class="hljs-selector-pseudo">:last-child</span>,
          <span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">ul</span><span class="hljs-selector-pseudo">:last-child</span>,
          <span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">ol</span><span class="hljs-selector-pseudo">:last-child</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;
          }
          <span class="hljs-selector-class">.blog-post</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">4rem</span>;
          }
          <span class="hljs-selector-class">.blog-post-title</span> {
            <span class="hljs-attribute">margin-bottom</span>: .<span class="hljs-number">25rem</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
            <span class="hljs-attribute">text-align</span>: center;
          }
          <span class="hljs-selector-class">.blog-post-meta</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1.25rem</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#999</span>;
            <span class="hljs-attribute">text-align</span>: center;
          }
          <span class="hljs-selector-class">.blog-footer</span> {
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">2.5rem</span> <span class="hljs-number">0</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#999</span>;
            <span class="hljs-attribute">text-align</span>: center;
            <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f9f9f9</span>;
            <span class="hljs-attribute">border-top</span>: .<span class="hljs-number">05rem</span> solid <span class="hljs-number">#e5e5e5</span>;
          }
          <span class="hljs-selector-class">.blog-footer</span> <span class="hljs-selector-tag">p</span><span class="hljs-selector-pseudo">:last-child</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;
          }
          <span class="hljs-selector-tag">input</span>{
              <span class="hljs-attribute">width</span>: <span class="hljs-number">45%</span> <span class="hljs-meta">!important</span>;
              <span class="hljs-attribute">display</span>: inline-block <span class="hljs-meta">!important</span>;
          }
          <span class="hljs-selector-tag">textarea</span> {
              <span class="hljs-attribute">width</span>: <span class="hljs-number">90%</span>;
              <span class="hljs-attribute">height</span>: <span class="hljs-number">150px</span>;
              <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span> <span class="hljs-number">20px</span>;
              <span class="hljs-attribute">box-sizing</span>: border-box;
              <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#ccc</span>;
              <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
              <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f8f8f8</span>;
              <span class="hljs-attribute">resize</span>: none;
          }
          <span class="hljs-selector-tag">textarea</span><span class="hljs-selector-pseudo">:focus</span>, <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>{
              <span class="hljs-attribute">outline</span>: none <span class="hljs-meta">!important</span>;
          }
          <span class="hljs-selector-id">#comment-section</span>{
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(178, 191, 214); 
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5em</span> <span class="hljs-number">2em</span>; <span class="hljs-attribute">width</span>: <span class="hljs-number">90%</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">15px</span>;
          }
          <span class="hljs-selector-id">#comment-section</span> > <span class="hljs-selector-tag">div</span> > <span class="hljs-selector-tag">p</span> {
            <span class="hljs-attribute">color</span>: black;
            <span class="hljs-attribute">display</span>:inline;
          }
          <span class="hljs-selector-tag">img</span>{
          <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
          <span class="hljs-attribute">float</span>: left;
          }
        </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">head</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">body</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">header</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-masthead"</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav"</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link active"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Home<span class="hljs-tag"></<span class="hljs-name">a</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">nav</span>></span>
              <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">header</span>></span>

          <span class="hljs-tag"><<span class="hljs-name">main</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"main"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>></span>

            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>></span>

              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-sm-12 blog-main"</span>></span>

                <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post"</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post-title"</span>></span>Realtime Comments With Pusher<span class="hljs-tag"></<span class="hljs-name">h2</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post-meta"</span>></span>January 1, 2018 by <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Jordan<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">p</span>></span>This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported
                  <span class="hljs-tag"></<span class="hljs-name">p</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"comment-section"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"comment"</span>></span>Comment<span class="hljs-tag"></<span class="hljs-name">h5</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"John Doe"</span> <span class="hljs-attr">required</span> <span class="hljs-attr">autofocus</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Johndoe@gmail.com"</span> <span class="hljs-attr">required</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">textarea</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"comment"</span>></span><span class="hljs-tag"></<span class="hljs-name">textarea</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-lg btn-primary"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"sendComment"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>></span>Comment<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">form</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">br</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"comment-section"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"comment in comments"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://merritos.com/img/team/maleTeam.jpg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"65px"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"65px"</span>></span>
               <span class="hljs-tag"><<span class="hljs-name">p</span>></span>&nbsp;&nbsp;{{comment.name}} &nbsp;<span class="hljs-tag">< {{<span class="hljs-attr">comment.email</span>}} ></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
               <span class="hljs-tag"><<span class="hljs-name">hr</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color:black"</span>></span>{{comment.comment}}<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                  <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"></<span class="hljs-name">div</span>></span>

            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>

          <span class="hljs-tag"></<span class="hljs-name">main</span>></span>

          <span class="hljs-tag"><<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-footer"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">p</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Back to top<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">footer</span>></span>

        <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">body</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">html</span>></span>
```

Now in the same file, paste the following code before the closing `body` tag of the HTML:

```
    <script>
      <span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> Vue({
        <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
        <span class="hljs-attr">data</span>: {
          <span class="hljs-attr">comments</span> : []
        },
        <span class="hljs-attr">created</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
          axios.get(<span class="hljs-string">'/comments'</span>).then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> { 
            <span class="hljs-keyword">this</span>.comments = response.data.items ? response.data.items : [] 
          })
        },
        <span class="hljs-attr">methods</span>: {
            <span class="hljs-attr">sendComment</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">index</span>) </span>{
              <span class="hljs-keyword">let</span> comment = {
                <span class="hljs-attr">name</span>: <span class="hljs-keyword">this</span>.$refs.username.value,
                <span class="hljs-attr">email</span>: <span class="hljs-keyword">this</span>.$refs.email.value,
                <span class="hljs-attr">comment</span>: <span class="hljs-keyword">this</span>.$refs.comment.value
              }

              axios.post(<span class="hljs-string">'/comment'</span>, comment).then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> { 
                <span class="hljs-keyword">this</span>.$refs.username.value = <span class="hljs-string">''</span>,
                <span class="hljs-keyword">this</span>.$refs.email.value = <span class="hljs-string">''</span>,
                <span class="hljs-keyword">this</span>.$refs.comment.value = <span class="hljs-string">''</span>
              })
            }
        }
      })
    <<span class="hljs-regexp">/script></span>
```

Above we have the Vue.js code for our application and this is a summary of what it does:

- We instantiate a comments array that will hold all the available comments.
- In the `created()` method, we use [Axios](https://www.npmjs.com/package/axios) to pull in all the comments available from the API and store it in the `comments` array.
- In the `sendComment` method, we send a request to the API to create a new `comment`.

We can build our application at this stage and visit <http://localhost:9000>, we should see this:

```
    $ go run comments.go

```

Our application should display like this:

![](//images.ctfassets.net/1es3ne0caaid/2m7H3gsZCMIkEKGueSEuK/d014e440249b413da2ed8f509d91eae1/go-comments-homepage.png)

### Making comments display in realtime

The next thing we need to do is make sure the comments are displayed in realtime. To do this, we need to trigger an event every time a new comment is added. We will do this in the backend using the [Pusher Go library](https://github.com/pusher/pusher-http-go).

To pull in the Pusher Go library run the following command:

```
    $ go get github.com/pusher/pusher-http-go

```

Next let’s import the library. In our `models.go` file do the following in the imports statement:

```
    <span class="hljs-keyword">package</span> models

    <span class="hljs-keyword">import</span> (
        <span class="hljs-comment">// [...]</span>

        pusher <span class="hljs-string">"github.com/pusher/pusher-http-go"</span>
    )
```

In the same file, before the `type` definition, paste in the following code:

```
    <span class="hljs-comment">// [...]</span>

    <span class="hljs-keyword">var</span> client = pusher.Client{
        AppId:   <span class="hljs-string">"PUSHER_APP_ID"</span>,
        Key:     <span class="hljs-string">"PUSHER_APP_KEY"</span>,
        Secret:  <span class="hljs-string">"PUSHER_APP_SECRET"</span>,
        Cluster: <span class="hljs-string">"PUSHER_APP_CLUSTER"</span>,
        Secure:  <span class="hljs-literal">true</span>,
    }

    <span class="hljs-comment">// [...]</span>
```

Here, we have initialized the Pusher client using the credentials from our earlier created app.

> ⚠️ Replace `PUSHER_APP_*` keys with your Pusher app credentials.

Next, let’s trigger an event every time a comment is saved to the database. Replace the `PushComment` function with the following code:

```
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB, name <span class="hljs-keyword">string</span>, email <span class="hljs-keyword">string</span>, comment <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int64</span>, error)</span></span> {
        sql := <span class="hljs-string">"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"</span>
        stmt, err := db.Prepare(sql)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }

        <span class="hljs-keyword">defer</span> stmt.Close()

        result, err2 := stmt.Exec(name, email, comment)
        <span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err2)
        }

        newComment := Comment{
            Name:    name,
            Email:   email,
            Comment: comment,
        }

        client.Trigger(<span class="hljs-string">"comment-channel"</span>, <span class="hljs-string">"new-comment"</span>, newComment)
        <span class="hljs-keyword">return</span> result.LastInsertId()
    }
```

In this newer version of the function we create a `newComment` object that holds information for the last comment that was saved to the database. Whenever a new comment is created, we will send it to the Pusher channel `comment-channel` to be triggered on the event `new-comment`.

**Displaying data in realtime on the client**To receive comments we have to register the [Pusher JavaScript Client](https://github.com/pusher/pusher-js) in our frontend code. Add this line of code inside the head tag of our HTML in the index.html file:

```
    <script src="https://js.pusher.com/4.1/pusher.min.js"></script>

```

Next we will register a Pusher instance in the `created()` life cycle hook:

```
    created: function() {

        <span class="hljs-keyword">const</span> pusher = <span class="hljs-built_in">new</span> Pusher(<span class="hljs-string">'PUSHER_APP_KEY'</span>, {
            cluster: <span class="hljs-string">'PUSHER_APP_CLUSTER'</span>,
            encrypted: <span class="hljs-literal">true</span>
        });

        <span class="hljs-keyword">const</span> channel = pusher.subscribe(<span class="hljs-string">'comment-channel'</span>);

        channel.bind(<span class="hljs-string">'new-comment'</span>, data => {
          this.comments.push(data)
        });

        <span class="hljs-comment">// [...]    </span>
    }
```

> ⚠️ Replace the `PUSHER_APP_*` keys with the credentials for your Pusher application.

In the code above, we are creating a Pusher instance and then subscribing to a channel. In that channel we are listening for the `new-comment` event.

Now we can run our application:

```
    $ go run comments.go

```

We can point a web browser to this address <http://localhost:9000> and we should see the application in action:

![](//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif)

## Conclusion

In this article, we looked at how to build a realtime comment system using Go, Vue.js, and Pusher Channels. The source code to the application is available on [GitHub.](https://github.com/neoighodaro/realtime-comments-go-pusher)

This post first appeared on the [Pusher blog](https://pusher.com/tutorials/live-comments-go-vuejs).