Repository navigation
Does this code use gcloud.datastore the way it should? #365
Description
Activity
- addedtype: questionRequest for information or clarification. Not an issue.Request for information or clarification. Not an issue.api: datastoreIssues related to the Datastore API.Issues related to the Datastore API.
on Jan 29, 2015 I found the following hard issues:
req.param.authorshould bereq.params.author.console.log('Initial book error', err);should beif(err) console.log('Initial book error', err);- Add
credentialsto datastore initialization.
Here's an updated version that fixes the issues above and features a bit more documentation. In addition to this, there are certain issues such as lack of braces on if statements, lack of new lines in if statements which aren't killers here and allow the application to still be functional, but differ from the more strict style guides we have adopted in the gcloud-node library. I haven't fixed those in this code and tried to assume the style that was already given to me.
If you would like me to update the following snippet with those style guidelines, please let me know.
var express = require('express'); var bodyParser = require('body-parser'); var gcloud = require('gcloud'); var util = require('util'); var app = express(); var projectId = process.env.PROJECT_ID; // E.g. 'grape-smasher-902' var keyFile = process.env.GCLOUD_KEYFILE; // E.g. '~/.secrets/key.json' if (!projectId) { throw new Error("The project ID must be set in the environment variable PROJECT_ID"); } // Initial book data var books = [ { name: "War and Peace", author: "Leo Tolstoy" }, { name: "The Count of Monte Cristo", author: "Alexandre Dumas" } ]; // Initialize the store var dataset = gcloud.datastore.dataset({ projectId: projectId, credentials: require(keyFile) }); // Add a book function addBook(transaction, book) { transaction.save({ key: dataset.key('Book'), data: book }); } // Find books that match a given book's values (or get all books) function findBooks(transaction, book, cb) { var query = dataset.createQuery(['Book']); book = book || {}; if (book.author) query = query.filter('author = ', book.author); if (book.name) query = query.filter('name = ', book.name); transaction.runQuery(query, function (err, entities, endCursor) { if (err) return cb(err); cb(null, entities); }); } // Ensure a book exists in the datastore, otherwise create it. function ensureExists(book, cb) { dataset.runInTransaction(function (transaction, done) { findBooks(transaction, book, function (err, entities) { if (!err && entities.length === 0) { addBook(transaction, book); } done(); }); }, cb); } // Query books by a certain author function booksQuery(req, res, author) { dataset.runInTransaction(function (transaction, done) { findBooks(transaction, { author: author }, function (err, books) { if (err) return res.sendStatus(500); res.json(books.map(function (entity) { return entity.data; })); done(); }); }, function (err) { if (err) return res.sendStatus(500); }); } // Add Express middle-ware app.use(bodyParser.json()); // Add routes app.get("/", function(req, res) { res.send('Hello World!'); }); // GET /books returns all books app.get("/books", function (req, res) { booksQuery(req, res); }); // GET /books/Leo%20Tolstoy returns all books where author="Leo Tolstoy" app.get("/books/:author", function (req, res) { booksQuery(req, res, req.params.author); }); // Create a book if it does not exist with a given name and author. app.put("/books", function (req, res) { if (!req.body || !req.body.name || !req.body.author) return res.sendStatus(400); ensureExists({ author : req.body.author, name: req.body.name }, function (err) { if (err) return res.sendStatus(500); res.sendStatus(200); }); }); // Ensure all initial books are created or already exist in the datastore. books.forEach(function (book) { ensureExists(book, function (err) { if(err) console.log('Initial book error', err); }); }); // Start the server var server = app.listen(8080, function () { console.log('Server running at http://localhost:%s', server.address().port); });
This example originally came from me.
I made some significant modifications to this file:
The file is now in a repository here, repeated here:
I have significantly simplified the example, removing the code to pre-initialize the datastore as it unnecessarily complicated the example. The population of the data will be handled elsewhere.
I didn't include the credentials because this assumes you will be authenticated via the metadata store (running in GCE or Managed VMs).
var express = require('express'); var bodyParser = require('body-parser'); var gcloud = require('gcloud'); var app = express(); // Initialize the store var dataset = gcloud.datastore.dataset({ projectId: process.env.PROJECT_ID }); // Add a book function addBook(book, cb) { dataset.save({ key: dataset.key('Book'), data: book }, cb); } // Find all books function findAllBooks(cb) { dataset.runQuery(dataset.createQuery(['Book']), cb); } // Find an author's books function findAuthorBooks(author, cb) { dataset.runQuery(dataset.createQuery(['Book']).filter("author = ", author), cb); } // Add middle-ware app.use(bodyParser.json()); // Add routes app.get("/", function(req, res) { res.send('Hello World!\n'); }); // GET /books returns all books app.get("/books", function (req, res) { findAllBooks(function (err, books) { if (err) return res.sendStatus(500); res.json(books.map(function (entity) { return entity.data; })); }); res.json(books); }); // GET /books/Leo%20Tolstoy returns all books where author="Leo Tolstoy" app.get("/books/:author", function (req, res) { findAuthor(req.params.author, function (err, books) { if (err) return res.sendStatus(500); res.json(books.map(function (entity) { return entity.data; })); }); }); // Create a book with a given name and author app.put("/books", function (req, res) { if (!req.body || !req.body.name || !req.body.author) return res.sendStatus(400); addBook({ author: req.body.author, name: req.body.name }, function (err) { if (err) return res.sendStatus(500); res.sendStatus(200); }); }); // Start server var server = app.listen(8080, function () { console.log('Server running at http://localhost:%s', server.address().port); });
Looks awesome now! 👍
Thanks!
One issue I brought up with JJ privately but wanted to bring up publicly is the projectId. The initialization of the datastore looks like:
var dataset = gcloud.datastore.dataset({ projectId: process.env.PROJECT_ID })
This seems unnecessary to me as, if you are in GCE or Managed VMs, the project id can be retrieved from the metadata store [http://metadata.google.internal/computeMetadata/v1/project/project-id] as in,
curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/project/project-idCould we make the default project id the value of requesting the project-id from the metadata server?
Chuck.
I might be mistaken but in Google App Engine and/or Managed VMs you can use the
process.env.GAE_LONG_APP_IDenvironment variable to get theprojectId.Good idea to automatically use the environment's project ID. I believe we did that way back, then I forgot to re-implement. Left a to do on the PR that is tidying up the GAE story: #328
Thanks. I will close this issue as the review is done.
- added a commit that references this issue
on Sep 16, 2022 39 remaining items
- added a commit that references this issue
on Feb 23, 2026 - added a commit that references this issue
on Feb 24, 2026 - added 2 commits that reference this issue
on Feb 25, 2026 - added a commit that references this issue
on Mar 5, 2026 - added 2 commits that reference this issue
on Mar 23, 2026 - added a commit that references this issue
on Mar 27, 2026 - added a commit that references this issue
on May 5, 2026
Comments appreciated.
If it doesn't we might want to consider updating documentation to avoid whatever problems it might have.
/cc @stephenplusplus @ryanseys