Using Cloud Datastore with Go

This page of the Go Bookshelf tutorial shows how the sample app stores its persistent data in Google Cloud Datastore. The sample code for this step provides examples of how to create, read, update, and delete (CRUD) data stored in Cloud Datastore.

This page is part of a multi-page tutorial. To start from the beginning and read the setup instructions, go to Go Bookshelf app.

Configuring settings

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


    cd $GOPATH/src/


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

  2. Open config.go for editing.

  3. Uncomment this line:

    // DB, err = configureDatastoreDB("projectid")
  4. Replace "projectid" with your project ID.

  5. Save and close config.go.

Cloud Datastore is a fully managed service that is automatically initialized and connected to your App Engine app. No further configuration is required.

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. Enter this address in your web browser:


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:


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 GCP Console, go to the Versions page for App Engine.

    Go to the Versions page

  2. Select the checkbox for the non-default app version you want to delete.
  3. Click Delete 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

This diagram shows how the application components work together.

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:


// 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 class="form-group">
    <label for="author">Author</label>
    <input class="form-control" name="author" id="author" value="{{.Author}}">
  <div class="form-group">
    <label for="publishedDate">Date Published</label>
    <input class="form-control" name="publishedDate" id="publishedDate" value="{{.PublishedDate}}">
  <div class="form-group">
    <label for="description">Description</label>
    <input class="form-control" name="description" id="description" value="{{.Description}}">
  <div class="form-group">
    <label for="image">Cover Image</label>
    <input class="form-control" name="image" id="image" type="file">
  <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}}">

Handling form submissions

When a user fills in the Add book form and then clicks Save, the createHandler function starts the process of sending the submitted data to Cloud Datastore by passing it to the bookshelf.DB.AddBook method:


// 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_datastore.go, performs a Put() operation to add the user's submitted data to the datastore:

// AddBook saves a given book, assigning it a new ID.
func (db *datastoreDB) AddBook(b *Book) (id int64, err error) {
	ctx := context.Background()
	k := datastore.IncompleteKey("Book", nil)
	k, err = db.client.Put(ctx, k, b)
	if err != nil {
		return 0, fmt.Errorf("datastoredb: could not put Book: %v", err)
	return k.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 datastore.NewQuery("Book") creates a query that selects all entities with kind equal to "Book". A single app can store more than one kind in the datastore. The query includes .Order("Title"), which specifies that the results should be ordered alphabetically by book title.

The query is executed by calling GetAll, which takes the query object and appends all the returned entities to the books slice. Finally, the ID field is set on the returned entities:

// ListBooks returns a list of books, ordered by title.
func (db *datastoreDB) ListBooks() ([]*Book, error) {
	ctx := context.Background()
	books := make([]*Book, 0)
	q := datastore.NewQuery("Book").

	keys, err := db.client.GetAll(ctx, q, &books)

	if err != nil {
		return nil, fmt.Errorf("datastoredb: could not list books: %v", err)

	for i, k := range keys {
		books[i].ID = k.ID

	return books, nil
Was this page helpful? Let us know how we did:

Send feedback about...