Go’s Web Forms and App Engine’s datastore

Satish Manohar Talim
5 min readSep 5, 2015

--

You need to know about Go’s template package before proceeding further.

Handling Web Forms

I would like to store information about an author and his/her quote on the Google App Engine for users to retrieve the same later on. We need a way to process information submitted by me with a web form. The Go “http” package makes processing form data easy.

Program quote.go

Inside the folder “$GOPATH/src/github.com/SatishTalim” create a folder “quotes”. Replace “SatishTalim” with your GitHub id.

Next inside the “quotes” folder, create a file named “quote.go”, and give it the following contents:

This app has two handlers: the path “/” is mapped to “root”, which displays a web form. The path “/quote” is mapped to “quote”, which displays the data submitted by the web form.

The “quote” function gets the form data by calling “r.FormValue” and passes it to “quoteTemplate.Execute” that writes the rendered template to the “http.ResponseWriter”. In the template code, the content is automatically filtered to escape HTML special characters. The automatic escaping is a property of the “html/template” package, as distinct from “text/template”.

The line “{{html .}}” in const “quoteTemplateHTML”, “html” is a predefined global function that returns the escaped HTML equivalent of the textual representation of its arguments, “.” in this case.

Creating the Configuration File

An App Engine application has a configuration file called “app.yaml”. Inside the “quotes” directory, create a file named “app.yaml” with the following contents:

Testing the form

You can now test it with the web server included with the App Engine SDK.

The application’s directory should contain the files “quote.go” and “app.yaml”.

From the “$GOPATH/src/github.com/SatishTalim” directory run the following command, to compile your app and start the development web server:

goapp serve quotes/

The web server is now running, listening for requests on port 8080. Test the application by visiting the following URL in your web browser: http://localhost:8080/.

You enter the author’s name and his/her quote and click on “Submit Quote” button. Next you should see the information that you had typed, on the screen.

The appengine package

Usage:

import “google.golang.org/appengine”

Package appengine provides basic functionality for the Google App Engine.

Jason Buberel — Product Manager, Go — Language, Tools & Ecosystem at Google has this to say:

The article on Using the Datastore is for a Google App Engine (GAE) version 1 where you import “appengine/datastore”. It requires the use of an “appengine.Context”.

However, if you are importing “google.golang.org/appengine/datastore” it requires you to use “context.Context”.

What you are seeing is a set of libraries in transition. We know this is a problem, and we have plans to clean up the docs soon.

Our plan is to “encourage” all existing GAEv1 applications to eventually transition from importing “appengine/” to importing “google.golang.org/appengine”. There are a few non-compatible differences between the two (such as which “Context to use”). But we will be supporting the legacy “appengine” import for some time to come.
The legacy “appengine” package can only be used within an App Engine v1 application (GAEv1). Whereas imports of “google.golang.org/appengine” packages can be used in GAEv1 and on Managed VMs applications (which will eventually be launched as GAEv2).

NewContext

func NewContext(req *http.Request) context.Context

“NewContext” returns a “Context” for an in-flight HTTP request.

Usage:

context := context.NewContext(r)

The appengine/datastore package

Usage:

import “google.golang.org/appengine/datastore”

Package appengine/datastore provides a client for App Engine’s datastore service.

Using the Datastore

Let us refer to the Datastore Reference for details. From this reference, we have:

Data is written to the Datastore in objects known as entities. Each entity has a key that uniquely identifies it. An entity can optionally designate another entity as its parent; the first entity is a child of the parent entity. The entities in the Datastore thus form a hierarchically structured space similar to the directory structure of a file system. An entity’s parent, parent’s parent, and so on recursively, are its ancestors; its children, children’s children, and so on, are its descendants. An entity without a parent is a root entity.

Entities descended from a common ancestor are said to belong to the same entity group; the common ancestor’s key is the group’s parent key, which serves to identify the entire group. Queries over a single entity group, called ancestor queries, refer to the parent key instead of a specific entity’s key.

In Go, Datastore entities are created from struct values. The structure’s fields become the properties of the entity. To create a new entity, you set up the value you wish to store, create a key, and pass them both to “datastore.Put()”.

For the quotes application, we want to store quotes posted by users. Each quote includes the author’s name and his/her quote (as a message).

To represent this data we create a Go struct named “Quote”:

type Quote struct {
Author string
Message string
}

Now that we have a data type for quotes, the application can create new “Quote” values and put them into the datastore. The new version of the “quote” handler does just that:

The above function creates a new Quote value, setting its Author and Message fields with the data posted by the user.

The “NewKey()” function is:

func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key

We use it as:

datastore.NewKey(c, “Quote”, “default_quote”, 0, nil)

The “NewIncompleteKey()” function is:

func NewIncompleteKey(c context.Context, kind string, parent *Key) *Key

We use it as:

key := datastore.NewIncompleteKey(c, “Quote”, quoteKey(c))

Datastore entities are the unit of storage and are associated with a key.

We set the same parent key on every Quote entity to ensure each Quote is in the same entity group. Queries across the single entity group will be consistent. Putting an entity into the datastore under a new incomplete key will cause a unique key to be generated for that entity.

The “Get” and “Put” functions load and save an entity’s contents.

Retrieving the Stored Quotes With datastore.Query

The datastore package provides a Query type for querying the datastore and iterating over the results.

The “fetch”” handler queries the datastore for quotes:

Here’s our modified “quote.go” program.

The development web server (on your local computer) uses a local version of the datastore for testing your application, using temporary files. The data persists as long as the temporary files exist, and the web server does not reset these files unless you ask it to do so.

The command:

goapp serve quotes/

also starts the admin server at http://localhost:8000/. Click Datastore Viewer in the left navigation pane to view your local Datastore contents. You should see all the quotes that you have entered.

You can also see all the quotes by typing http://localhost:8080/fetch in your browser.

--

--

Satish Manohar Talim

Senior Technical Evangelist at GoProducts Engineering India LLP, Co-Organizer GopherConIndia. Director JoshSoftware and Maybole Technologies. #golang hobbyist