Web App with Go and Aerospike

This is my first stab at Go programming language and I have to admit that the initial experience has been quite different from when I learned PowerBuilder (What’s that, right? Google it.), Java, C#, Ruby on Rails, Javascript, Node.js and other web technologies.

In any case, I do want to share a simple and straightforward web app I have put together in Go that uses Aerospike as the database. This app:

  • Connects to the Aerospike database
  • Allows you to enter name on a web page
  • Stores the name you entered into the Aerospike database
  • Reads the name back and displays it on the web page like so “Hi Dash!”

In the coming sections we’ll look at the code snippets but you may also download the source from GitHub

Before we dive into code though, here’s how I’ve structured it:

  • Aerospike configuration is stored separately in a config file
  • The two views are in their own html files
  • The main executable program is coded within a separate file

Just enough separation of code that I thought would be helpful.

Step 1: Install Go

Step 2: Install Aerospike Database

Step 3: Install Aerospike Go Client

  • Create a new folder (e.g. go-aerospike-app) for this new application you’re going to write.

  • Open terminal window and browse to this application folder. Then, run go get github.com/aerospike/aerospike-client-go

    Note: If you get error(s) related to GOPATH or other environment variables, click here and resolve them before moving forward.

Step 4: Configuration

  • Create a new file named aerospike_config.go and add the following code. Make sure you save this file in the application root folder.

    <

    pre>package main

type aerospikeDBConfig struct { clusterIP string clusterPort int defaultNamespace string defaultSet string }

func aerospikeDBParams() aerospikeDBConfig { params := aerospikeDBConfig{ “127.0.0.1”, 3000, “test”, “aerospike”, } return params }

*What's happening?* You are defining a structure **aerospikeDBConfig** that holds Aerospike config parameters and a function **aerospikeDBParams()** that creates an instance of it and makes it available for use in the main program.
  • Update clusterIP and clusterPort variables such that they point to your instance of the Aerospike Server.

Step 5: Setup Views

  • Create a new file named form.html and add the following code. Make sure you save this file in the application root folder. (Note: The style is inlined for simplicity and this is not what I encourage or recommend doing.)

<div style='background-color:#efefef;color:#000000;width:100%;height:100%;text-align:center;padding-top:50px;font-family:verdana,arial'>
<form action='/write' method='post'>
<label>Enter your name:</label>
<input type='text' name='myname'/>
<input type='submit' value='Go!'></input>
</form>
</div>

What’s happening? You’ve written just enough html to display a form with one input field and one submit button. Notice the form submit action write — it is described in the main program code below.

  • Create a new file named display.html and add the following code. Make sure you save this file in the application root folder. (Note: The style is inlined for simplicity and this is not what I encourage or recommend doing.)

<div style='background-color:#efefef;color:#000000;width:100%;height:100%;text-align:center;padding-top:50px;font-family:verdana,arial'>
<h2>Hi {{printf "%s" .Name}}!</h2>
</div>

What’s happening? You’ve written a simple html template that displays “Hi [name]” — how this template gets the value of “.Name” is described in the main program code below.

Step 6: Write Main Program

  • Create a new file named lets.go and add the following code. Make sure you save this file in the application root folder.

    <

    pre>package main

import ( “fmt” “net/http” “html/template” . “github.com/aerospike/aerospike-client-go” )

// Structure that holds User info such as Name type User struct { Name string }

// View handler to display user input form — this is the default view that gets rendered when visiting root route http://localhost:8080 func viewHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set(“Content-Type”, “text/html”) t, err := template.ParseFiles(“form.html”) if err != nil { // Handle error fmt.Fprintf(w, “\nError parsing form.html”) return } t.Execute(w, nil) }

// Action handler that gets triggered when submitting user input form. It connects to Aerospike cluster, writes a record, reads the record written and closes the connection func writeHandler(w http.ResponseWriter, r *http.Request) { // Establish connection to the cluster client, err := NewClient(aerospikeDBParams().clusterIP,aerospikeDBParams().clusterPort) if err != nil { // Handle error fmt.Fprintf(w, “\nConnection to Aerospike cluster Failed. Please check clusterIP and clusterPort in aerospike_config.go and try again.”) return } // Setup connection close to be called at the end of the process — this is done using ‘defer’ defer client.Close()

// Extract form value entered var name string name = r.FormValue(“myname”)

// Create Aerospike key instance passing it namespace, set and key — the values are hard-coded for simplicity key, err := NewKey(aerospikeDBParams().defaultNamespace,aerospikeDBParams().defaultSet, “hello”) if err != nil { // Handle error fmt.Fprintf(w, “\nError creating instance of Aerospike Key.”) return }

// Create Aerospike bin instance bins := BinMap{ “greet”: name, }

// Write record in the database err = client.Put(nil, key, bins) if err != nil { // Handle error fmt.Fprintf(w, “\nError writing record to the Aerospike database.”) return }

// Read (the same) record from the database rec, err := client.Get(nil, key) if err != nil { // Handle error fmt.Fprintf(w, “\nError reading record from the Aerospike database.”) return }

// Display value read from the dabatase t, err := template.ParseFiles(“display.html”) if err != nil { // Handle error fmt.Fprintf(w, “\nError parsing display.html.”) return }

// Create an instance of User and set Name attribute to the value retrieved from the database user := User{Name: rec.Bins[“greet”].(string)} t.Execute(w, user) }

// Entry point that gets executed when the program starts. It sets up the two routes – root and write – and also starts the web server on http://localhost:8080 func main() { fmt.Printf(“The server is running on http://localhost:8080. Press Ctrl-C to exit…\n”) http.HandleFunc(“/”, viewHandler) http.HandleFunc(“/write”, writeHandler) http.ListenAndServe(“:8080”, nil) }

What’s happening? This is the meat of your application. It contains the entry point func main() that gets executed when the program starts. It sets up root and write routes and also starts the web server. To handle the two routes, it also defines the respective handlers: func viewHandler() that displays user input form (as per form.html) which gets rendered when visiting root route http://localhost:8080 and func writeHandler() that gets triggered when submitting user input form. The purpose of this Write handler is to connect to Aerospike cluster, write the record, read the record written, display it (as per ‘display.form’) and close the connection to Aerospike cluster.

Step 7: Run The Application

  • Open terminal window, browse to the application folder and run ‘go run *.go‘ — you should see the following:

‘The server is running on http://localhost:8080. Press Ctrl-C to exit…’

  • Open browser window – then browse to http://localhost:8080. You should see a simple web page with a form to enter your name.

  • Type your name and click on Go!

  • You should see “Hi [name]” on the web page!

Done and Done!

Leave a Reply