Using MongoDB with Go

This part of the Go Bookshelf tutorial shows how the sample app stores its persistent data in a MongoDB database.

This page is part of a multi-page tutorial. To start from the beginning and see instructions for setting up, go to Go Bookshelf App.

Running MongoDB on Google Compute Engine

If you haven't set up a Mongo instance, you can create a managed one running on top of Google Cloud Platform using mLab. If you'd rather use an unmanaged MongoDB instance running on Compute Engine, check out Bitnami's preconfigured click-to-deploy solution.

For best performance, make sure your MongoDB instance is hosted on Google Cloud Platform and located in the same region as your app.

Configuring settings

  1. Go to the directory that contains the sample code:

    Linux/Mac OS X

    cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/getting-started/bookshelf
    

    Windows

    cd %GOPATH%\src\github.com\GoogleCloudPlatform\golang-samples\getting-started\bookshelf
    

  2. Open config.go for editing.

  3. Uncomment the following lines:

    // var cred *mgo.Credential
    // DB, err = newMongoDB("localhost", cred)
    
  4. Replace localhost with the external IP address address of your MongoDB instance. This is the external IP that you noted in the earlier section. You can also find the external IP address by running gcloud compute instances list.

  5. Save and close config.go.

Running the app on your local machine

  1. Run the app to start a local web server:

    cd app
    go run app.go auth.go template.go
    
  2. In your web browser, enter this address:

    http://localhost:8080

Now you can browse the app's web pages and add, edit, and delete books.

Press Control+C to exit the local web server.

Deploying the app to the App Engine flexible environment

  1. In the app directory, enter this command to deploy the sample:

    gcloud app deploy
    
  2. In your web browser, enter this address. Replace [YOUR_PROJECT_ID] with your project ID:

    https://[YOUR_PROJECT_ID].appspot.com
    

If you update your app, you can deploy the updated version by entering the same command you used to deploy the app the first time. The new deployment creates a new version of your app and promotes it to the default version. The older versions of your app remain, as do their associated VM instances. Be aware that all of these app versions and VM instances are billable resources.

You can reduce costs by deleting the non-default versions of your app.

To delete an app version:

  1. In the Cloud Platform Console, go to the App Engine Versions page.

    Go to the Versions page

  2. Click the checkbox next to the non-default app version you want to delete.
  3. Click the Delete button at the top of the page to delete the app version.

For complete information about cleaning up billable resources, see the Cleaning up section in the final step of this tutorial.

Application structure

The following diagram shows the how the sample application components are connected to each other:

Bookshelf app deployment process and structure

Understanding the code

This section walks you through the application code and explains how it works.

Handling user submissions with forms

When a user clicks Add Book, the browser navigates to /books/add, and the addFormHandler function in app/app.go renders a form where the user can add a book:

r.Methods("GET").Path("/books/add").
	Handler(appHandler(addFormHandler))
// addFormHandler displays a form that captures details of a new book to add to
// the database.
func addFormHandler(w http.ResponseWriter, r *http.Request) *appError {
	return editTmpl.Execute(w, r, nil)
}

Image of add/edit Form

The form includes text input fields for Title, Author, Date Published and Description; it also includes some others, which are used in other sections of the tutorial:

<form method="post" enctype="multipart/form-data" action="/books{{if .}}/{{.ID}}{{end}}">
  <div class="form-group">
    <label for="title">Title</label>
    <input class="form-control" name="title" id="title" value="{{.Title}}">
  </div>
  <div class="form-group">
    <label for="author">Author</label>
    <input class="form-control" name="author" id="author" value="{{.Author}}">
  </div>
  <div class="form-group">
    <label for="publishedDate">Date Published</label>
    <input class="form-control" name="publishedDate" id="publishedDate" value="{{.PublishedDate}}">
  </div>
  <div class="form-group">
    <label for="description">Description</label>
    <input class="form-control" name="description" id="description" value="{{.Description}}">
  </div>
  <div class="form-group">
    <label for="image">Cover Image</label>
    <input class="form-control" name="image" id="image" type="file">
  </div>
  <button class="btn btn-success">Save</button>
  <input type="hidden" name="imageURL" value="{{.ImageURL}}">
  <input type="hidden" name="createdBy" value="{{.CreatedBy}}">
  <input type="hidden" name="createdByID" value="{{.CreatedByID}}">
</form>

Handling form submissions

When a user fills in the form and clicks Save, the following code handles the form's HTTP POST /books action. The code inserts the new book into the database by calling the bookshelf.DB.AddBook() function.

r.Methods("POST").Path("/books").
	Handler(appHandler(createHandler))
// createHandler adds a book to the database.
func createHandler(w http.ResponseWriter, r *http.Request) *appError {
	book, err := bookFromForm(r)
	if err != nil {
		return appErrorf(err, "could not parse book from form: %v", err)
	}
	id, err := bookshelf.DB.AddBook(book)
	if err != nil {
		return appErrorf(err, "could not save book: %v", err)
	}
	go publishUpdate(id)
	http.Redirect(w, r, fmt.Sprintf("/books/%d", id), http.StatusFound)
	return nil
}

The AddBook function, defined in db_mongo.go, performs an Insert() operation to add the user's submitted data as a document to the MongoDB collection.

// AddBook saves a given book, assigning it a new ID.
func (db *mongoDB) AddBook(b *Book) (id int64, err error) {
	id, err = randomID()
	if err != nil {
		return 0, fmt.Errorf("mongodb: could not assign an new ID: %v", err)
	}

	b.ID = id
	if err := db.c.Insert(b); err != nil {
		return 0, fmt.Errorf("mongodb: could not add book: %v", err)
	}
	return id, nil
}

When the user clicks Books, the browser navigates to the /books page, and the ListBooks method displays all the books in the datastore.

The statement db.c.Find(nil) creates a query that selects all documents in the collection. The query includes .Sort("title"), which specifies that the results should be ordered alphabetically by book title.

The query is executed by calling All, which appends all the returned documents to the result slice.

// ListBooks returns a list of books, ordered by title.
func (db *mongoDB) ListBooks() ([]*Book, error) {
	var result []*Book
	if err := db.c.Find(nil).Sort("title").All(&result); err != nil {
		return nil, err
	}
	return result, nil
}

Monitor your resources on the go

Get the Google Cloud Console app to help you manage your projects.

Send feedback about...