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 HTML
output 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 Pusher
client 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, home
is 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 Pusher
using 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!
Top comments (0)