Go, Martini and Postgres Tutorial - Part 1

Getting Going (get it)

I spent the weekend playing around with Go and a web framework called Martini and thought I'd put something together on building a simple todos app (I know how original) with it. Currently I plan for this to be a 3 parter but no promises.

Setting up Go

First things first, we're going to need to install go. Head on over to the Go site and download the installer. With that taken care of we need to setup our GOPATH. This is used to specify the location of our workspace. For me, I set mine up to use a folder called go in documents but change as you feel necessary. Open up either your .bashrc or .zshrc and add the following: export GOPATH=~/Documents/go and then append $GOPATH/bin to your path.
Create a new folder in you go directory called src and then inside of their let's create a new folder called todos.

Martini Time

Create a file called server.go in the root of todos and then add the following code:

package main

import (
	"github.com/go-martini/martini"
)

func main() {
	m := martini.Classic()
    m.Get("/", func() string {
    	return "Martini Time"
    })
    
    m.Run()
}

In the terminal run go get github.com/go-martini/martini then go run server.go and if you now visit localhost:3000 you should see the most beautiful page you have ever seen.
A couple of the notes on the code above for those that maybe haven't written any Go.
Import allows us to import multiple packages from other sources whether they be hosted on github, local packages or packages that are part of go. Whenever bringing in a package from github we need to run go get github.com/example/package in order to use it.
That strange := before martini.Classic() allows go to assign a variable of the correct type. Go is able to infer the type based on the value that the variable is being assigned to. For instance:

var y string = "hello world"
y := "hello word"

are equivalent.
We then create a handler for a get request to "/" and return "Martini Time". We need to specify in the function declaration that the return type is a string.

Hooking it up to Postgresql

In order to make our lives a bit easier we're going to import a few more packages for connecting to our database. Go ahead and add to the import declaration up the top

_ "github.com/lib/pq"
"github.com/coopernurse/gorp"
"database/sql"
"log"

Go and go get the github packages. The reason we add _ before the first line is because we won't be referring to it by name, we just need it's initializations. You might notice a couple of errors with the code. You can go ahead and ignore them for now.
We're going to create a struct to represent our Todo so add the following

type Todo struct {
	Id int64
    Created int64
    Title string
    Description string
}

Let's get the database hookedup. We're going to create a new function called initDb that will return a gorp.dbMap (gorp is an orm for go).

func initDb() *gorp.DbMap {
	db, err := sql.Open("postgres", "user=admin dbname=martinitodos sslmode=disable")
	checkErr(err, "postgres.Open failed")

	dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
	table := dbmap.AddTableWithName(Todo{}, "todos").SetKeys(true, "Id")

	table.ColMap("Title").SetNotNull(true)
	table.ColMap("Description").SetNotNull(true)

	err = dbmap.CreateTablesIfNotExists()
	checkErr(err, "Create tables failed")

	return dbmap
}

and then in our main function add

dbmap := initDb()
defer dbmap.Db.Close()

right after m := martini.Classic()

In initDb we first establish a connection to the database using the Postgres driver (don't forget to create a database) then create a dbmap using gorp. We then add the table todos using the struct that we specified above for the schema and use SetKeys to tell it that it will have an auto incrimenting primary key. We then add some null constraints and finally create the table if it doesn't already exist. Otherwise it will just be added to the mapper for use in our app.
We defer dbmap.Db.Close() because we don't want to close the connection to the database immiediately, instead waiting for the main function to close before closing it.

Showing some Todos

Go ahead and delete the following code

m.Get("/", func() string {
    return "Martini Time"
})

We need to add a couple more packages to import so go ahead and add

"github.com/martini-contrib/render"
"time"
"html/template"

(Don't forget to go get)

Render is going to allow us to not only render static pages and start to turn this into a dynamic app. Let's set it up. Underneath defer dbmap.Db.Close() add

m.Use(render.Renderer(render.Options {
    Layout: "layout",
    Funcs: []template.FuncMap {
    {
        "formatTime": func(args ...interface{}) string { 
                t1 := time.Unix(args[0].(int64), 0)
                return t1.Format("Mon Jan _2 2006")
            },
        },
    },
}))

This is telling render to use layout.tmpl for the layout file and creates a nice little view helper so that our timestamps don't look quite so ugly.
Create a new folder called templates (this is where render looks by default for it's templates) and create layout.tmpl. Add the following to it

<html>
<head>
  <title>Go Todos</title>
  <link rel="stylesheet" href="http:////maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
</head>
<body>
  <nav class="navbar navbar-default" role="navigation">
    <div class="container-fluid">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">Martini Todos</a>
      </div>

      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav navbar-right">
          <li><a href="/">All Todos</a></li>
          <li><a href="todos/new">New Todo</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <div class='container'>
    {{ yield }}
  </div>

</body>
</html>

Because we want our site to look like every other site on the internet we've included bootstrap in our html.

Create a new folder called todos in templates and a new file called index.tmpl. This will be where we render all of our lovely todos. Make it look something like

<h1>All Todos</h1>

<ul>
{{ range . }}
  <li><a href="/todos/{{ .Id }}">{{ .Title }}</a> from {{ .Created | formatTime }}</li>
{{ end }}
</ul>

Add the following to server.go underneath the renderer setup

m.Get("/", func(r render.Render) {
    var todos []Todo
    _, err := dbmap.Select(&todos, "select * from todos order by created")
    if err != nil {
        log.Fatalln(err)
    }
    r.HTML(200, "todos/index", todos)
})

Here we're just saying go and get all the todos, render the todos/index template and pass over all the todos. Then in the template we use {{ range . }}{{ end }} to iterate over them all. Lovely.
If we visit localhost:3000 we won't see any todos because we haven't created any yet. So let's do that.

Creating a Todo

Create a new file in todos called new.tmpl and add the following to it:

{{ range . }}
<p>{{ .FieldNames }} is {{ .Message }}</p>
{{ end }}
<form role="form" action="/todos" method="post">
  <div class="form-group">
    <label for="title">Title</label>
    <input type="text" name="Title" class="form-control" id="title"
    placeholder="Enter title">
  </div>
  <div class="form-group">
    <label for="description">Description</label>
    <textarea type="text" name="Description" class="form-control"
    id="description" placeholder="Enter description"></textarea>
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

And then below our index action in server.go add in

m.Get("/todos/new", func(r render.Render) {
    r.HTML(200, "todos/new", nil)
})

which will render the form when we visit localhost:3000/todos/new

We're going to need a new package so go ahead and add "github.com/martini-contrib/binding", "time" and "strconv"to your import. This will handle validations and bindings for us. We need to make one change to our todos struct to make it play nice with the validations on binding so change it to look like

type Todo struct {
	Id int64
	Created int64
	Title string `form:"Title" binding:"required"`
	Description string `form:"Description" binding:"required`
}

Now it's time for our create action. Underneath where we render the new page add in

m.Post("/todos", binding.Form(Todo{}), func(todo Todo, errors binding.Errors, r render.Render) {
  if errors != nil {
      r.HTML(422, "todos/new", errors)
  } else {
      t1 := &Todo{Title: todo.Title, Description: todo.Description, Created: time.Now().Unix()}
      err := dbmap.Insert(t1)
      if err != nil {
          log.Println(err)
      }
      r.Redirect("todos/" + strconv.FormatInt(t1.Id, 10), 302)
  }
})

Firstly we check if there are any errors, if there are we rerender the form and add the errors to the top of the page. Otherwise we create a new Todo using the todo struct using the data from the form. We insert it into the database and redirect to the show page for that todo. We don't have a show page yet to let's do that.

The Todo

Create a new file called show.tmpl inside of the todos folder. Make it look like

<h1>{{ .Title }}</h1>
<p>{{ .Description}}</p>
<p>{{ .Created | formatTime}}</p>

Now in our server.go underneath where we created a todo add the following

m.Get("/todos/:id", func(params martini.Params, r render.Render) {
    var todo Todo
    err := dbmap.SelectOne(&todo, "select * from todos where id = :id",
    map[string]interface{} {"id": params["id"]})
    if err != nil {
        r.HTML(404, "error", "This todo is not found")
    }
    r.HTML(200, "todos/show", todo)
})

Note: The docs say you can just do "select * from todos where id=?", params["id"] but I was running into issues doing it that way so decided on this. You can try it if you like.
We use params martini.Params so that we can gain access to the params parsed over in the URL. We use this Id to query the database, get back the relevant todo and render the show template. If the Todo is not found we render a 404. Magical.

Going further (seriously I am too funny)

Next time I want to look at using oAuth with martini and maybe touch on a little bit of Ajax. We shall see. I hope this kind of makes some sort of sense. I'm learning as I go and having a huge amount of fun with Go so far. If you want to see the code for this you can find it here