Using Cloud Datastore with Node.js

This page of the Node.js 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 see instructions for setting up, go to Node.js Bookshelf App.

Configuring settings

In the nodejs-getting-started/2-structured-data directory, create a config.json file with this content:

  "DATA_BACKEND": "datastore"

Replace [YOUR_PROJECT_ID] with your project ID.

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

Installing dependencies

Install dependencies in the nodejs-getting-started/2-structured-data directory:

  • Using npm:

    npm install
  • Using yarn:

    yarn install

Running the app on your local machine

  1. Start a local web server using npm or yarn:

    • Using npm:

      npm start
    • Using yarn:

      yarn start
  2. In your web browser, enter the following address:


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

Deploying the app to the App Engine standard environment

  1. Deploy the sample app:

    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. By default the App Engine standard environment scales to 0 instances when there is no incoming traffic to a version, thus unused versions should not cost anything. However, all of these app versions are still billable resources.

See the Cleaning up section in the final step of this tutorial for more information on cleaning up billable resources, including non-default app versions.

Understanding the code

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

Handling user submissions with forms

The add/edit form allows users to add and edit book submissions within the app.

Image of add/edit Form

The HTML form is created using Pug, which is a Node.js template engine. The following Pug template specifies that the form include text input fields for "Title", "Author", "Date Published" and "Description":

block content
  h3 #{action} book
      label(for="title") Title
      input.form-control(type="text", name="title", id="title", value=book.title)
      label(for="author") Author
      input.form-control(type="text", name="author", id="author",
      label(for="publishedDate") Date Published
      input.form-control(type="text", name="publishedDate", id="publishedDate", value=book.publishedDate)
      label(for="description") Description
      input.form-control(type="text", name="description", id="description", value=book.description)
    button.btn.btn-success(type="submit") Save

Handling form submissions

When a user clicks Add Book, the following code in books/crud.js sends the user to the app's /add page, which displays the add/edit form.

router.get('/add', (req, res) => {
  res.render('books/form.pug', {
    book: {},
    action: 'Add'

When a user fills out the Add book form and then clicks Save, the following code in books/crud.js handles the form's HTTP POST action. This initiates the process of sending the submitted data to Cloud Datastore by passing the data to the model.create function.'/add', (req, res, next) => {
  const data = req.body;

  // Save the data to the database.
  getModel().create(data, (err, savedData) => {
    if (err) {

The books/model-datastore.js file contains the code that performs CRUD functions for data stored in Cloud Datastore. For example the model.create statement calls the create function in model-datastore.js, which sends the user's submitted data to the update function with a null value as the first parameter, which indicates that this is a new book submission.

module.exports = {
  delete: _delete,

Here is the update function that does the actual work of saving the user's submitted data to Cloud Datastore. The toDatastore and fromDatastore helper functions are used to translate the user's submitted data from the app's format to Cloud Datastore's extended entity property format.

function update (id, data, cb) {
  let key;
  if (id) {
    key = ds.key([kind, parseInt(id, 10)]);
  } else {
    key = ds.key(kind);

  const entity = {
    key: key,
    data: toDatastore(data, ['description'])
    (err) => { =;
      cb(err, err ? null : data);

After users have added books, clicking the Books link navigates to the /books page, which lists all the books currently stored in Cloud Datastore. The page calls the books/model-datastore.js file which initializes a Datastore dataset named ds and then sets the kind variable to Book, which is the type of entity being stored by this app. A single app can store more than one kind in the datastore.

const ds = Datastore({
  projectId: config.get('GCLOUD_PROJECT')
const kind = 'Book';

Next, the list function does the work of listing all the books using data retrieved from Cloud Datastore. The statement var q = ds.createQuery([kind]) creates a query named q that will select all books in the datastore. The following additional properties are added to the query:

  • .limit(limit) sets the maximum amount of results to return per page.
  • .order('title') specifies that the results should be ordered alphabetically by book title.
  • .start(token) specifies the starting point for handling pagination and allows additional pages to be requested based on the value of token.

The query is then executed by calling ds.runQuery, which takes the query object q as the query to be run for its first parameter. The second parameter of ds.runQuery is a callback that is invoked with (err, books, nextPageToken).

function list (limit, token, cb) {
  const q = ds.createQuery([kind])

  ds.runQuery(q, (err, entities, nextQuery) => {
    if (err) {
    const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false;
    cb(null,, hasMore);
Was this page helpful? Let us know how we did:

Send feedback about...