loading...
Cover image for Writing A Simple Electronic Realtime Poll with Go

Writing A Simple Electronic Realtime Poll with Go

lexmartinez profile image Lex Martinez ・4 min read

Originally published on my blog

Golang, known on the streets as Go, is gaining popularity since was announced in 2009, nine years after Go have demonstrated to be a simple but powerfull programming language and the cherry on top of the cake, pretty easy to learn. In this post we are going to create a realtime poll with it server side writen in Go, which was created as part of academic exercise in order to learn that awesome language.

The original project idea was taken from this awesome article writen by Christian Nwamba at codementor.io, basically in his post he built the realtime poll with NodeJS, to be honest that post liked me so much, and given that I wanted to taste something of Go flavors, I decided create my own poll version with Golang.

First Steps

To be honest, I never write nothing with Go before, so my first step was installed it into my computer, which was pretty easy, was just a thing of navigate into golang.org website and download the right installer for my operative system, then use that installation wizard to complete the process, in a few minutes I had the Go core installed in my computer and ready to code. In order to test it, I used the Hello, Wold! example published on Golang website:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World")
}

After check that all works good using the command go run hello.go my next step was to prepare my code editor (Visual Studio Code) to work with Go, basically I found this great extension on the marketplace, after installed it, I was ready to Go.

My Plan

Essentially as the Christian Nwamba post, the project was divided in a few layers, three to be more specific, first the server which delivers the views and receive the votes, second the Pusher integration in order to give to the project that realtime feature, finally the views including the vote screen and results screen, for those views we use a few lines of JavaScript and HTML and SemanticUI to style it, in this post we are going to talk about the first two layers.

The Server

As we said before, our server side is in charge to deliver the views and receive the votes, so the backend design has the following endpoints :

Method Path Description
GET / Votes Screen
POST / Votes reception
GET /results Results Chart Screen

To implement those endpoints and create the simple routing that we need, the net/http was used, then our main function looks like this

func main() {
    http.HandleFunc("/", base)
    http.HandleFunc("/results", results)

    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    } else {
        fmt.Println("Polling server running at localhost:8080")
    }
}

In that snippet basically we tell to Go which routes we are going to dispatch and which function should handle those requests, also start the server at 8080 port. Let's see the handler functions separatelly:

Results Handler

The results handler function is pretty basic, is in charge to deliver the results screen with results chart inside of it, the chart render and the Pusher listener is part of our frontend features so in our server side we just need to response with the HTMLoutput stream, to do that I wanted to use a template engine implementation like Pug, fortunately I found this pretty package github.com/Joker/jade the syntax is almost the same of pug so basically was all that we needed.

func results(w http.ResponseWriter, r *http.Request) {
    dat, err := ioutil.ReadFile("./templates/results.pug")
    if err != nil {
        fmt.Printf("ReadFile error: %v", err)
        return
    }

    tmpl, err := jade.Parse("results", string(dat))
    if err != nil {
        fmt.Printf("Parse error: %v", err)
        return
    }

    keys := Keys{
        Key:   os.Getenv("APP_KEY"),
        Cluster: os.Getenv("APP_CLUSTER"),
    }

    goTpl, err := template.New("html").Parse(tmpl)
    if err != nil {
        fmt.Printf("\nTemplate parse error: %v", err)
        return
    }
    err = goTpl.Execute(w, keys)
    if err != nil {
        fmt.Printf("\nExecute error: %v", err)
        return
    }
}

Some footnotes, we read our external template files with io/ioutil package, also as parameters we are injecting a few enviroment variables to Pusherclient connection, that enviroment variables are loaded from an external .env with github.com/alexsasharegan/dotenv package.

Base Handler

The base function is going to handle the root requests, i.e. Deliver the vote screen and receive the votes (then send them to Pusher), let's see the base handler implementation

func base(w http.ResponseWriter, r *http.Request) {
    if r.Method == http.MethodPost {
        vote(w,r)
    } else if r.Method == http.MethodGet {
        home(w,r)
    } else {
        w.WriteHeader(http.StatusBadRequest)
        w.Write([]byte("400 - Unsupported Request Method!"))
    }
}

In this function in essence, we are routing our requests to two different subhandlers based on request method, the first one, homeis basically the same to results handler, here is the vote handler implementation

func vote (w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    option := r.Form.Get("option")

    client := pusher.Client{
        AppId: os.Getenv("APP_ID"),
        Key: os.Getenv("APP_KEY"),
        Secret: os.Getenv("APP_SECRET"),
        Cluster: os.Getenv("APP_CLUSTER"),
        Secure: true,
    }

    data := map[string]string{"vote": option}
    client.Trigger("tv-shows", "vote-event", data)

    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte("Success vote for: " + option))
}

In this snippet we receive the request data and get option field a.k.a our vote, then send it through Pusherusing the pusher API package github.com/pusher/pusher-http-go.

Pusher is an awesome realtime notifications API that use websockets, if you want to use it and configure you have to sign up (there are a free mode) and configure your app, for more information about the app setup see the Pusher docs

That's it! our poll server is alive!

  • Complete source code for this exercise could be found on this Github repo
  • Thanks for reading! comments, suggestions and DMs are welcome!

Posted on by:

lexmartinez profile

Lex Martinez

@lexmartinez

I'm a full-stack Javascript Developer based in Medellín, Colombia, with 9+ years of experience building web/mobile apps. The years i've been working as freelance JS Developer.

Discussion

pic
Editor guide