Using MongoDB with Node.js

This part of the Node.js Bookshelf tutorial shows how the sample app stores its persistent data in a MongoDB database.

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.

Running MongoDB on Google Cloud Platform

If you haven't set up a Mongo instance, you can create a managed one running on top of Google Cloud Platform using mLab. If you'd rather use an unmanaged MongoDB instance running on Compute Engine, check out Bitnami's preconfigured click-to-deploy solution.

For best performance, make sure your MongoDB instance is hosted on Google Cloud Platform and located in the same region as your app.

Configuring settings

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

{
  "GCLOUD_PROJECT": "[YOUR_PROJECT_ID]",
  "DATA_BACKEND": "mongodb",
  "MONGO_URL": "[YOUR_MONGO_URI]",
  "MONGO_COLLECTION": "books"
}
  1. Replace [YOUR_PROJECT_ID] with your project ID.

  2. Replace [YOUR_MONGO_URI] with your MongoDB connection URI. For more information on MongoDB's connection URI format, [see the MongoDB documentation].(https://docs.mongodb.com/manual/reference/connection-string/).

    • mLab users: your MongoDB connection URI is listed on your deployment's information page.
    • Bitnami users: by default, your MongoDB username is root and your password is the Bitnami base password on the Compute Engine Instance page.

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 this address.

    http://localhost:8080

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

Deploying the app to the App Engine flexible 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:

    https://[YOUR_PROJECT_ID].appspot.com
    

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

    Go to the Versions page

  2. Click the checkbox next to the non-default app version you want to delete.
  3. Click the Delete button at the top of the page 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

The following diagram shows the how the sample application components are connected to each other.

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

The add/edit form enables users to add and edit book submissions within the app. The add/edit form looks like this:

Image of add/edit Form

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

block content
  h3 #{action} book
  form(method="POST")
    .form-group
      label(for="title") Title
      input.form-control(type="text", name="title", id="title", value=book.title)
    .form-group
      label(for="author") Author
      input.form-control(type="text", name="author", id="author", value=book.author)
    .form-group
      label(for="publishedDate") Date Published
      input.form-control(type="text", name="publishedDate", id="publishedDate", value=book.publishedDate)
    .form-group
      label(for="description") Description
      input.form-control(type="text", name="description", id="description", value=book.description)
    .form-group
    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 clicks Save, the following code in books/crud.js handles the form's HTTP POST action and initiates the process of sending the submitted data to MongoDB by passing it to the model.create() function.

router.post('/add', (req, res, next) => {
  const data = req.body;

  // Save the data to the database.
  getModel().create(data, (err, savedData) => {
    if (err) {
      next(err);
      return;
    }
    res.redirect(`${req.baseUrl}/${savedData.id}`);
  });
});

The file named books/model-mongodb.js contains the code that performs CRUD functions for data stored in MongoDB. For example the call to model.create in the preceding code calls the create function in model-mongodb.js.

The following code defines the create and update functions:

function create (data, cb) {
  getCollection((err, collection) => {
    if (err) {
      cb(err);
      return;
    }
    collection.insert(data, {w: 1}, (err, result) => {
      if (err) {
        cb(err);
        return;
      }
      const item = fromMongo(result.ops);
      cb(null, item);
    });
  });
}
function update (id, data, cb) {
  getCollection((err, collection) => {
    if (err) {
      cb(err);
      return;
    }
    collection.update(
      { _id: new ObjectID(id) },
      { '$set': toMongo(data) },
      { w: 1 },
      (err) => {
        if (err) {
          cb(err);
          return;
        }
        read(id, cb);
      }
    );
  });
}

The fromMongo and toMongo helper functions are used to translate the user's submitted data from the app's format to MongoDB's document format. This is necessary because the app expects the id to be named id whereas MongoDB provides it as _id.

function fromMongo (item) {
  if (Array.isArray(item) && item.length) {
    item = item[0];
  }
  item.id = item._id;
  delete item._id;
  return item;
}

function toMongo (item) {
  delete item.id;
  return item;
}

After users have added books, clicking the Books link navigates to the /books page, which lists all the books stored in MongoDB. This page uses the model's list function to do the work of querying for all the books.

function list (limit, token, cb) {
  token = token ? parseInt(token, 10) : 0;
  if (isNaN(token)) {
    cb(new Error('invalid token'));
    return;
  }
  getCollection((err, collection) => {
    if (err) {
      cb(err);
      return;
    }
    collection.find({})
      .skip(token)
      .limit(limit)
      .toArray((err, results) => {
        if (err) {
          cb(err);
          return;
        }
        const hasMore =
          results.length === limit ? token + results.length : false;
        cb(null, results.map(fromMongo), hasMore);
      });
  });
}

This function takes a limit and token parameter to control pagination. The function then calls MongoDB and finds all book items and the translates the results into the application's format.

Monitor your resources on the go

Get the Google Cloud Console app to help you manage your projects.

Send feedback about...