Backbone.js Tutorial: Backbone.sync

20 Dec 2012 | By Alex Young | Tags backbone.js mvc node backgoog

Preparation

Before starting this tutorial, you’ll need the following:

  • alexyoung / dailyjs-backbone-tutorial at commit c1d5a2e7cc
  • The API key from part 2
  • The “Client ID” key from part 2
  • Update app/js/config.js with your keys (if you’ve checked out my source)

To check out the source, run the following commands (or use a suitable Git GUI tool):

git clone git@github.com:alexyoung/dailyjs-backbone-tutorial.git
cd dailyjs-backbone-tutorial
git reset --hard c1d5a2e7cc

Google’s Tasks API

To recap: the point of this tutorial series is to build a Backbone.js single page application that uses client-side JavaScript to communicate with Google’s authentication and to-do list APIs. Got that? Good!

Google provides access to our to-do lists through two APIs:

When loading Google’s JavaScript, the browser is bestowed with a global called gapi that provides access to various objects and methods. In the last part, I quietly included a call to gapi.client.load that loads the tasks API:

gapi.client.load('tasks', 'v1', function() { /* Loaded */ });

This can be found in app/js/gapi.js. The remaining challenge before building the interface is to implement a new Backbone.sync method that uses gapi to communicate with the Tasks and Tasklists APIs.

Backbone.sync Structure

I’ve already talked about the overall structure of Backbone.sync in part 2. The pattern I’ll use in these tutorials is fairly generic, so you could use the same approach to communicate with something other than Google’s APIs.

The sync method itself takes three arguments, the first of which is the method (create, update, delete, and read). We need to map method to something Google’s API can understand.

This is what we’ve got so far:

Backbone.sync = function(method, model, options) {
  options || (options = {});

  switch (method) {
    case 'create':
    break;

    case 'update':
    break;

    case 'delete':
    break;

    case 'read':
    break;
  }
};

Google’s Tasks API methods map to the Backbone method argument like this:

Google Tasks APIBackbone.sync MethodDescription
insertcreateCreate a new task.
updateupdateUpdate an existing task.
deletedeleteDelete a task.
listreadGet a list of tasks.

Even though Google’s API doesn’t look like the Rails 3-based RESTful API that Backbone.js is designed for out of the box, it’s still very close.

Making Requests with gapi

The gapi object makes requests using this pattern:

  • Call one of the gapi.client.tasks methods with the request content to get a request object
  • Call request.execute with a callback to send the request
  • The callback receives a response object, much like a standard Ajax request

Here’s what this looks like in reality:

var requestContent = {}
  , request
  , gapiResource;

gapiResource = 'tasks';
requestContent['tasklist'] = tasklistId; // Assuming we have one
requestContent['resource'] = model.toJSON();

// 'insert' is for creating new tasks
request = gapi.client.tasks[gapiResource].insert(requestContent);

// Send the request to the API
request.execute(function(res) {
  // Handle the response
});

Looking at this, it’s clear that we need two models: Task and TaskList. There also need to be two corresponding collections: Tasks and TaskLists.

Backbone models and collections have URLs – these are used for making API requests. Similarly, Google’s APIs have URLs: tasks and tasklists, so by using the model URL Backbone.sync can determine which API resource is required for a given request.

Models

Create a new directory called app/js/models and add task.js:

define(function() {
  var Task = Backbone.Model.extend({
    url: 'tasks'
  });

  return Task;
});

You’ll also want to create a app/js/models/tasklist.js:

define(function() {
  var TaskList = Backbone.Model.extend({
    url: 'tasklists'
  });

  return TaskList;
});

Collections

Create another new directory called app/js/collections and add tasklists.js:

define(['models/tasklist'], function(TaskList) {
  var TaskLists = Backbone.Collection.extend({
    model: TaskList
  , url: 'tasklists'
  });

  return TaskLists;
});

We’re going to use the TaskList collection later on to load your task lists.

Making API Requests

Open up app/js/gapi.js and add a new line after line 36:

app.views.auth.$el.hide();
$('#signed-in-container').show();
self.trigger('ready'); // This one

This 'ready' event will be used to signify that authentication was successful, and the Tasks API is ready for use. Next, add the following two lines to Backbone.sync, inside the read switch case:

case 'read':
  var request = gapi.client.tasks[model.url].list(options.data);
  Backbone.gapiRequest(request, method, model, options);
break;

This code creates a request, and then Backbone.gapiRequest will execute it and delegate the response.

Here’s the most basic Backbone.gapiRequest implementation:

Backbone.gapiRequest = function(request, method, model, options) {
  var result;
  request.execute(function(res) {
    if (res.error) {
      if (options.error) options.error(res);
    } else if (options.success) {
      result = res.items;
      options.success(result, true, request);
    }
  });
};

All it does is run request.execute, which is provided by Google, and then maps the result to be compatible with Backbone’s API by running the success and error callbacks.

Just so you can see something is really happening, open app/js/app.js and make it load the TaskLists collection by changing the define invocation at the top:

define([
  'gapi'
, 'views/app'
, 'views/auth'
, 'collections/tasklists'
],

function(ApiManager, AppView, AuthView, TaskLists) {

Now add this to the connectGapi method:

this.apiManager.on('ready', function() {
  self.collections.lists.fetch({ data: { userId: '@me' }, success: function(res) {
    _.each(res.models, function(model) {
      console.log(model.get('title'));
    });
  }});
});

That code uses Underscore’s each method to iterate over each “model” returned by Backbone.sync, which is called by the TaskList collection.

Run the server with npm start, and visit http://localhost:8080. If you run it in a browser that supports console, then you should see your task lists printed out.

My task lists.

If you’ve got this working then you’re not far off building a real world Backbone.js app that communicates with Google’s APIs. The same concepts can be applied to other Google JavaScript APIs.

Summary

The full source for this tutorial can be found in alexyoung / dailyjs-backbone-tutorial, commit fcd653ec6.


blog comments powered by Disqus