Go is an extremely clean and fun language to work with and has a bunch of handy, modular, well documented packages out of the box. We can easily build a small web app using pure Go for handler functions, the net/http
library for routing and serving and mgo
as a MongoDB driver.
The simple Go web app that we will build will use MongoHQ and Heroku’s “Platform-as-a-Service,” (PaaS). At this service level, the vendor takes care of the underlying infrastructure for you, giving you only a platform with which to (build and) host your application(s). The only thing you have to worry about is your application itself.
Let us see how.
MongoDB
MongoDB is a high-performance, open source, schema-free, document-oriented database.
- MongoDB has the same concept of a “database” with which you are likely already familiar (or a schema for you Oracle folks). Within a MongoDB instance you can have zero or more databases, each acting as high-level containers for everything else.
- A database can have zero or more “collections“. A collection shares enough in common with a traditional “table” that you can safely think of the two as the same thing.
- Collections are made up of zero or more “documents“. Again, a document can safely be thought of as a “row”.
- A document is made up of one or more “fields“, which you can probably guess are a lot like “columns”.
- “Indexes” in MongoDB function much like their RDBMS counterparts.
- “Cursors” are different than the other five concepts but they are important enough. The important thing to understand about cursors is that when you ask MongoDB for data, it returns a cursor, which we can do things to, such as counting or skipping ahead, without actually pulling down data.
MongoHQ the hosted database
MongoHQ is a platform for MongoDB hosting on the web.
Sign Up
Sign up for a free account. When you fill up the online form, remember what you enter for default username and password – we will need this information later on.
Create a database
Next, create a free database in your MongoHQ account. I have created a database named godata
in my account. This database is at troup.mongohq.com
and port 10080.
The Mongo URI to connect to this database is: mongodb://:@troup.mongohq.com:10080/godata
.
Create a collection
For my database godata
I created a collection named user
.
Add documents
I then added many documents to this collection. Example of a document:
name: "Stefan Klaste", email: "klaste@posteo.de"
Dependencies
Go
I am assuming that you have Go already installed.
Workspaces
Go code must be kept inside a workspace. A workspace is a directory hierarchy with three directories at its root:
src
contains Go source files organized into packages (one package per directory),pkg
contains package objects, andbin
contains executable commands.
The go
tool builds source packages and installs the resulting binaries to the pkg
and bin
directories.
The src
subdirectory typically contains version control repositories (such as for Git) that track the development of one or more source packages.
The GOPATH environment variable
The GOPATH
environment variable specifies the location of your workspace.
To get started, create a workspace directory and set GOPATH
accordingly. Your workspace can be located wherever you like. Note that this must not be the same path as your Go installation.
On my Windows computer, I have set GOPATH=C:\go_projects\go
. Next I update my system environment variable PATH
to include my workspace bin
subdirectory i.e. PATH=%PATH%;%GOPATH%\bin;
My workspace folder structure:
C:/go_projects +---go +---bin +---pkg +---src
Package paths
The packages from the standard library are given short paths such as fmt
and net/http
. For your own packages, you must choose a base path that is unlikely to collide with future additions to the standard library or other external libraries. If you have a GitHub account at github.com/user
, that should be your base path. We will use github.com/user as our base path. Create a directory inside your workspace in which to keep source code. I have created the folder C:\go_projects\go\src\github.com\SatishTalim
.
Tip: Replace SatishTalim
with your own username.
mgo – Go driver for MongoDB
Of all the Go drivers available for MongoDB, mgo is the most advanced and well-maintained.
To install mgo
, make sure you have the bzr command available (bzr
should be installed prior to mgo
) and then run:
go get labix.org/v2/mgo
Our app gomongohq.go
This is a trivial Go web app that runs on Heroku. A user is prompted to enter a name. The web app accesses a MongoDB database on MongoHQ and returns to the user the email id of the name entered, if it exists.
List of imports
Our Go program gomongohq.go
is just one single go file; here’s the list of imports:
package main import ( "fmt" "html/template" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "net/http" "os" )
Person struct
The Person
struct
will hold our data fetched from the database.
type Person struct { Name string Email string }
Main
Here’s the main function where we set up the URL routes for the application, connect to the database, and start the web server:
func main() { http.HandleFunc("/", root) http.HandleFunc("/display", display) fmt.Println("listening...") err := http.ListenAndServe(GetPort(), nil) if err != nil { panic(err) } } // Get the Port from the environment so we can run on Heroku func GetPort() string { var port = os.Getenv("PORT") // Set a default port if there is nothing in the environment if port == "" { port = "4747" fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port) } return ":" + port }
http.HandleFunc(“/”, root)
When a user accesses our app, the app throws up a simple form for the user to enter a name whose email id he/she wishes to see. The function root
throws a page as declared by the constant rootForm
:
func root(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, rootForm) } const rootForm = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Your details</title> <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css"> </head> <body style="margin: 20px;"> <h2>A Fun Go App on Heroku to access MongoDB on MongoHQ</h2> <p>This simple app will fetch the email id of a person, if it's already there in the MongoDB database.</p> <p>Please enter a name (example: Stefan Klaste)</p> <form action="/display" method="post" accept-charset="utf-8" class="pure-form"> <input type="text" name="name" placeholder="name" /> <input type="submit" value=".. and query database!" class="pure-button pure-button-primary"/> </form> <div> <p><b>© 2014 RubyLearning. All rights reserved.</b></p> </div> </body> </html> `
The rootForm
uses Pure – a set of small, responsive CSS modules that you can use in every web project.
http.HandleFunc(“/display”, display)
var displayTemplate = template.Must(template.New("display").Parse(displayTemplateHTML)) func display(w http.ResponseWriter, r *http.Request) { // In the open command window set the following for Heroku: // heroku config:set MONGOHQ_URL= // mongodb://IndianGuru:password@troup.mongohq.com:10080/godata uri := os.Getenv("MONGOHQ_URL") if uri == "" { fmt.Println("no connection string provided") os.Exit(1) } sess, err := mgo.Dial(uri) if err != nil { fmt.Printf("Can't connect to mongo, go error %v\n", err) os.Exit(1) } defer sess.Close() sess.SetSafe(&mgo.Safe{}) collection := sess.DB("godata").C("user") result := Person{} collection.Find(bson.M{"name": r.FormValue("name")}).One(&result) if result.Email != "" { errn := displayTemplate.Execute(w, "The email id you wanted is: " + result.Email) if errn != nil { http.Error(w, errn.Error(), http.StatusInternalServerError) } } else { displayTemplate.Execute(w, "Sorry... The email id you wanted does not exist.") } } const displayTemplateHTML = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Results</title> <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css"> </head> <body> <h2>A Fun Go App on Heroku to access MongoDB on MongoHQ</h2> <p><b>{{html .}}</b></p> <p><a href="/">Start again!</a></p> <div> <p><b>© 2014 RubyLearning. All rights reserved.</b></p> </div> </body> </html> `
The function display
uses the html/template
package and the mgo
driver to access the database on MongoHQ. If the name is found in the database the function throws a page to the user with the email id for that name.
Usage of the mgo
driver revolves around the concept of sessions. To get started, obtain a session using the Dial function:
session, err := mgo.Dial(url)
This will establish one or more connections with the cluster of servers defined by the url parameter. From then on, the cluster may be queried and documents retrieved with statements such as:
c := session.DB(database).C(collection) err := c.Find(query).One(&result)
Once the session is not useful anymore, Close
must be called to release the resources appropriately.
Close terminates the session. It’s a runtime error to use a session after it has been closed.
SetSafe changes the session safety mode. If the safe parameter is nil
, the session is put in unsafe mode, and writes become fire-and-forget, without error checking. The unsafe mode is faster since operations won’t hold on waiting for a confirmation.
The following statement will make the session check for errors, without imposing further constraints:
session.SetSafe(&mgo.Safe{})
Safe returns the current safety mode for the session.
DB returns a value representing the named database (in our case godata
). If name is empty, the database name provided in the dialed URL is used instead. If that is also empty, “test” is used as a fallback.
C returns a value representing the named collection (in our case user
).
Insert inserts one or more documents in the respective collection.
Find prepares a query using the provided document. The document may be a map or a struct value capable of being marshalled with bson
. The map may be a generic one using interface{} for its key and/or values, such as bson.M
, or it may be a properly typed map. Providing nil
as the document is equivalent to providing an empty document such as bson.M{}
.
Further details of the query may be tweaked using the resulting Query value, and then executed to retrieve results using methods such as One
, For
, Iter
, or Tail
.
One executes the query and unmarshals the first obtained document into the result argument. The result must be a struct or map value capable of being unmarshalled into. This function blocks until either a result is available or an error happens.
Package bson is an implementation of the BSON specification for Go.
M is a convenient alias for a map[string]interface{}
map, useful for dealing with BSON in a native way. For instance: bson.M{"a": 1, "b": true}
Here’s the full code of our program:
Run the program locally
Make a new folder and cd
to it as follows:
$ mkdir $GOPATH/src/github.com/SatishTalim/gomongohq $ cd $GOPATH/src/github.com/SatishTalim/gomongohq
In this folder, copy the above file gomongohq.go
.
Now you can run the program with the go
tool:
$ cd $GOPATH/src/github.com/SatishTalim/gomongohq $ set MONGOHQ_URL=mongodb://IndianGuru:password@troup.mongohq.com:10080/godata $ go run gomongohq.go listening... INFO: No PORT environment variable detected, defaulting to 4747
Note: Please change the username IndianGuru
and password
above to your own username
and password
. Also troup.mongohq.com:10080
may be different for you.
You can access the URL locally in your browser: http://localhost:4747
The program would present a page that asks you to enter a name. Enter a name and click on the button. The app then throws a new page with the email id for the name you entered or an error message.
Great, it worked! Let us now deploy our app to Heroku.
Deploy app to Heroku
Create an account on Heroku
Please ensure that you are connected to the internet and then create an account on Heroku (obviously do this only once). If you don’t have one, then signup. It’s free and instant. A free account can have upto 5 apps without registering your credit card.
Install the Heroku Toolbelt
The Heroku Toolbelt ensures that you have access to the Heroku command-line client, Foreman which is used to run applications locally, and the Git revision control system that is used to deploy sites to Heroku.
Once installed, you’ll have access to the heroku
command from your command window. Log in using the email address and password you used when creating your Heroku account:
$ cd $GOPATH/src/github.com/SatishTalim/gomongohq $ heroku login Enter your Heroku credentials. Email: satish@rubylearning.org Password: Could not find an existing public key. Would you like to generate one? [Yn] Generating new SSH public key. Uploading ssh public key /Users/satish/.ssh/id_rsa.pub
Create a Procfile
Create a Procfile
to tell Heroku what command to run for the web
process in our app. The Procfile
should be created in the folder $GOPATH/src/github.com/SatishTalim/gomongohq
and contains:
web: gomongohq
Use Git
In order to deploy to Heroku we’ll need the app stored in Git. In the same folder i.e. $GOPATH/src/github.com/SatishTalim/gomongohq
type:
$ git init $ git add -A . $ git commit -m "code"
Install Godep
The recommended way to manage Go package dependencies on Heroku is with Godep, which helps build applications reproducibly by fixing their dependencies.
Let us install Godep
:
$ go get github.com/kr/godep
Now save your dependencies:
$ godep save
This will save a list of dependencies to the file Godeps/Godeps.json
, and copy their source code into Godeps/_workspace
.
Add these new files to git:
$ git add -A . $ git commit -m "dependencies"
Now we’re ready to ship this to Heroku.
Heroku deploy
Create a new Heroku app, telling it to use the Go Heroku Buildpack to build your Go code:
$ heroku create -b https://github.com/kr/heroku-buildpack-go.git Creating tranquil-bastion-1774... done, stack is cedar BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git http://tranquil-bastion-1774.herokuapp.com/ | git@heroku.com:tranquil-bastion-1774.git Git remote heroku added
Push the code to Heroku:
$ git push heroku master Initializing repository, done. Counting objects: 11, done. Delta compression using up to 4 threads. Compressing objects: 100% (8/8), done. Writing objects: 100% (11/11), 1.29 KiB | 0 bytes/s, done. Total 11 (delta 0), reused 0 (delta 0) -----> Fetching custom git buildpack... done -----> Go app detected -----> Installing go1.2... done -----> Running: godep go install -tags heroku ./... -----> Discovering process types Procfile declares types -> web -----> Compressing... done, 1.7MB -----> Launching... done, v4 http://tranquil-bastion-1774.herokuapp.com deployed to Heroku To git@heroku.com:tranquil-bastion-1774.git * [new branch] master -> master
Set MONGOHQ_URL
just once:
$ heroku config:set MONGOHQ_URL=mongodb://IndianGuru:password@troup.mongohq.com:10080/godata
Note: Please change the username IndianGuru
and password
above to your own username
and password
. Also troup.mongohq.com:10080
may be different for you.
Your app should be up and running. Visit it with:
$ heroku open Opening tranquil-bastion-1774... done
That’s it – you now have a running Go web app on Heroku!
You can visit this app here.
I hope this example gives a little more insight to Go newbies, at how to build real web applications in Go.
Do check out William Kennedy’s blog post “Running MongoDB Queries Concurrently With Go” where he talks about sessions.
Feel free to ask questions and give feedback in the comments section of this post. Thanks!
Reblogged this on narutosanjiv and commented:
Go sample webapp with mongodb on mongohq
i’m new in go and i have built a rest API with go and mysql but the complete code is just in one file and i’m from nodejs background and i want to make this api according to MVC design pattern, help me plz!
Great reaading