Storing Data

Save user-uploaded content in Cloud Datastore, a non-relational (NoSQL) database built for automatic scaling, high performance, and ease of application development.

This page and sample are part of an extended learning example of a simple blog application where users can upload posts. You should already be familiar with the Go programming language and basic web development. To start from the beginning, go to Building an App with Go.

Costs

There are no costs associated with running this tutorial. Running this sample app alone does not exceed your free quota.

Before you begin

If you have started from Building an App with Go, skip this section. Otherwise, complete the following steps:

  1. Complete the tasks in Before you begin in Setting Up Your Project and Application.

  2. In this example, you add code to the gophers-3 sample, which is the final product from Handling HTML Form Data.

    Download the gophers-3 sample and its dependencies to your local machine:

    go get -u -d github.com/GoogleCloudPlatform/golang-samples/appengine/gophers/gophers-3/...
    
  3. Change to the gophers-3 directory:

    cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/appengine/gophers/gophers-3
    

Structuring your application

This sample project has the following structure:

  • go-app/: Project root directory.

    • app.yaml: Configuration settings for your App Engine application.
    • main.go: Your application code.
    • index.html: HTML template to display your homepage.
    • static/: Directory to store your static files.
      • style.css: Stylesheet that formats the look of your HTML files.
      • gcp-gopher.svg: Gopher image.

Creating and filing a post

Classify each user-submitted post into three fields: the author's name, the message content, and the date and time the user posts the message.

  1. Add the following packages to your list of imports in your main.go file:

    "time"
    
    "google.golang.org/appengine/datastore"
    "google.golang.org/appengine/log"

  2. Define each user-submitted post as a data structure with three fields: Author, Message, and Posted.

    type Post struct {
    	Author  string
    	Message string
    	Posted  time.Time
    }
    

  3. Add the following fields to your templateParams data structure to update your dynamic HTML file.

    Message string
    
    Posts []Post

  4. Once the user submits the form and a POST request is made, handle the form submission by classifying the user-submitted form values into your Post data structure:

    post := Post{
    	Author:  r.FormValue("name"),
    	Message: r.FormValue("message"),
    	Posted:  time.Now(),
    }

Saving a post to Cloud Datastore

Save new values, otherwise known as entities, to Cloud Datastore with datastore.Put function.

  1. Add a new context in your indexHandler function to link all operations related to a given request together:

    ctx := appengine.NewContext(r)

    The appengine.NewContext function returns a context.Context value associated with the current request. This is an opaque value used by many functions in the Go App Engine SDK to communicate with the App Engine service.

  2. In Cloud Datastore, a key is a unique, auto-generated identifier. There are complete keys that point to an entity, and incomplete keys that point to an entity that is not yet stored in Cloud Datastore. When you create a key, you must specify the kind. Entities are of a given kind, similar to the way rows in relational databases belong to a given table. For more information, see the Entities, Properties, and Keys.

    Create a key of kind Post in your indexHandler function:

    key := datastore.NewIncompleteKey(ctx, "Post", nil)

    The last parameter in the NewIncompleteKey function is a *datastore.Key pointing to the parent of the key you're creating. For now, it will always be nil.

  3. Store the post entity in Cloud Datastore using the datastore.Put function:

    if _, err := datastore.Put(ctx, key, &post); err != nil {
    	log.Errorf(ctx, "datastore.Put: %v", err)
    
    	w.WriteHeader(http.StatusInternalServerError)
    	params.Notice = "Couldn't add new post. Try again?"
    	params.Message = post.Message // Preserve their message so they can try again.
    	indexTemplate.Execute(w, params)
    	return
    }

    The if statement handles errors by logging them, sending an error code, and updating the notice in index.html.

  4. Prepend the post that was just uploaded to your Posts[] array in templateParams{}:

    params.Posts = append([]Post{post}, params.Posts...)

Retrieving a post from Cloud Datastore

Query Cloud Datastore and iterate over the results.

  1. Construct a Query value that requests the twenty most recent Post objects in Posted descending order in your indexHandler function:

    q := datastore.NewQuery("Post").Order("-Posted").Limit(20)

    For more information on queries, see Cloud Datastore Queries.

  2. Retrieve the results by executing the Query and using its GetAll method to append results that match params.Posts:

    if _, err := q.GetAll(ctx, &params.Posts); err != nil {
    	log.Errorf(ctx, "Getting posts: %v", err)
    	w.WriteHeader(http.StatusInternalServerError)
    	params.Notice = "Couldn't get latest posts. Refresh?"
    	indexTemplate.Execute(w, params)
    	return
    }

Showing posts in your HTML template

In index.html, under your form, add the following lines to update your template with submitted posts:

{{with .Posts}}
<ol>
  {{range .}}
  <li><cite>{{.Author}}</cite><p>{{.Message}}</p></li>
  {{end}}
</ol>
{{end}}

Running your application locally

Run and test your application using the local development server (dev_appserver.py), which is included with Cloud SDK.

  1. From the project root directory where your application's app.yaml is located, start the local development server with the following command:

    dev_appserver.py app.yaml
    

    The local development server is now running and listening for requests on port 8080. Something go wrong?

  2. Visit http://localhost:8080/ in your web browser to view the app.

    Final

Running the local development server (dev_appserver.py)

To run the local development server, you can either run dev_appserver.py by specifying the full directory path or you can add dev_appserver.py to your PATH environment variable:

  • If you installed the original App Engine SDK, the tool is located at:

    [PATH_TO_APP_ENGINE_SDK]/dev_appserver.py
    
  • If you installed the Google Cloud SDK, the tool is located at:

    [PATH_TO_CLOUD_SDK]/google-cloud-sdk/bin/dev_appserver.py
    

    Tip: To add the Google Cloud SDK tools to your PATH environment variable and enable command-completion in your shell, you can run:

    [PATH_TO_CLOUD_SDK]/google-cloud-sdk/install.sh
    

For more information about running the local development server including how to change the port number, see the Local Development Server reference.

Making code changes

The local development server watches for changes in your project files, so it recompiles and re-launches your application after you make code changes.

  1. Try it now: Leave the local development server running and then try editing index.html to change "The Gopher Network" to something else.

  2. Reload http://localhost:8080/ to see the change.

Deploying your application

Deploy your application to App Engine using the following command from the project root directory where app.yaml is located:

gcloud app deploy

Viewing your application

To launch your browser and view your application at http://[YOUR_PROJECT_ID].appspot.com, run the following command:

gcloud app browse

Next steps

Congratulations! You built an application that can store and retrieve data from Cloud Datastore.

Next, learn how to authenticate users to your application using Firebase Authentication.

Send feedback about...

App Engine standard environment for Go