Backbone REST Persistence — Server Communication
In this tutorial, you will learn about Backbone REST Persistence. We cover key concepts, practical examples, and best practices to help you master this topic.
Backbone provides built-in REST persistence through Backbone.sync. Models and Collections communicate with RESTful APIs using standard HTTP methods. Understanding this system lets you connect your Backbone app to any backend that speaks REST.
What You'll Learn
You'll learn how Backbone maps CRUD to HTTP, configure URLs, handle server responses, customize sync behavior, and integrate with REST APIs.
Why It Matters
Most applications need server persistence. Backbone's REST integration handles the communication layer, letting you focus on application logic instead of writing AJAX boilerplate.
Real-World Use
A threat intelligence platform uses Backbone Models to represent IoCs (Indicators of Compromise). model.save() creates a new IoC on the server. collection.fetch() loads the latest threats. The REST API is the single source of truth.
flowchart LR
A[model.save] --> B{Has id?}
B -->|No| C[POST /api/resource]
B -->|Yes| D[PUT /api/resource/:id]
E[model.fetch] --> F[GET /api/resource/:id]
G[model.destroy] --> H[DELETE /api/resource/:id]
I[collection.fetch] --> J[GET /api/resource]
Default URL Mapping
Backbone determines URLs from the Model's urlRoot or Collection's url.
var Task = Backbone.Model.extend({
urlRoot: '/api/tasks',
defaults: { title: '', completed: false }
});
var TaskList = Backbone.Collection.extend({
model: Task,
url: '/api/tasks'
});
var task = new Task({ id: 5 });
console.log('Model URL:', task.url());
// /api/tasks/5
var tasks = new TaskList();
console.log('Collection URL:', tasks.url);
// /api/tasks
// Individual model within collection
tasks.add(task);
console.log('Model URL via collection:', task.url());
// /api/tasks/5
Expected output:
Model URL: /api/tasks/5
Collection URL: /api/tasks
Model URL via collection: /api/tasks/5
Creating Resources (POST)
When a Model has no id, save() sends a POST request.
var Task = Backbone.Model.extend({
urlRoot: '/api/tasks',
defaults: { title: '', completed: false }
});
var task = new Task({ title: 'New task' });
console.log('Is new:', task.isNew());
task.save(null, {
success: function(model, response) {
console.log('Created:', response);
console.log('Server assigned ID:', model.id);
console.log('Is new after save:', model.isNew());
},
error: function(model, response) {
console.error('Create failed:', response.status, response.statusText);
}
});
Expected output (assuming server returns {id: 101, title: "New task", completed: false}):
Is new: true
Created: {id: 101, title: "New task", completed: false}
Server assigned ID: 101
Is new after save: false
Updating Resources (PUT)
When a Model has an id, save() sends a PUT request to /api/tasks/:id.
var Task = Backbone.Model.extend({
urlRoot: '/api/tasks'
});
var task = new Task({ id: 1, title: 'Original title', completed: false });
// Update specific fields
task.save({ title: 'Updated title' }, {
success: function(model, response) {
console.log('Updated:', model.get('title'));
console.log('Server response:', response);
},
error: function(model, response) {
console.error('Update failed:', response.status);
}
});
Expected output:
Updated: Updated title
Server response: {id: 1, title: "Updated title", completed: false}
Patching Resources (PATCH)
Use {patch: true} to send only changed attributes.
var Task = Backbone.Model.extend({
urlRoot: '/api/tasks'
});
var task = new Task({ id: 1, title: 'Task', completed: false, priority: 'medium' });
// Send only the changed field
task.save({ completed: true }, {
patch: true,
success: function() {
console.log('Patched successfully');
console.log('Sent only:', task.changedAttributes());
}
});
Expected output:
Patched successfully
Sent only: {completed: true}
Reading Resources (GET)
model.fetch() sends GET, collection.fetch() sends GET to the list URL.
var Task = Backbone.Model.extend({ urlRoot: '/api/tasks' });
var TaskList = Backbone.Collection.extend({
model: Task,
url: '/api/tasks'
});
// Fetch single model
var task = new Task({ id: 1 });
task.fetch({
success: function(model) {
console.log('Loaded task:', model.get('title'));
console.log('All attributes:', model.toJSON());
}
});
// Fetch collection
var tasks = new TaskList();
tasks.fetch({
success: function(collection) {
console.log('Loaded', collection.length, 'tasks');
collection.each(function(t) {
console.log('-', t.get('title'));
});
}
});
Expected output (server-dependent):
Loaded task: First task
All attributes: {id: 1, title: "First task", completed: false}
Loaded 3 tasks
- First task
- Second task
- Third task
Deleting Resources (DELETE)
model.destroy() sends a DELETE request.
var Task = Backbone.Model.extend({ urlRoot: '/api/tasks' });
var task = new Task({ id: 1 });
task.on('destroy', function() {
console.log('Model destroyed event fired');
});
task.destroy({
success: function(model, response) {
console.log('DELETE successful');
console.log('Model ID after destroy:', model.id);
console.log('Model isNew:', model.isNew());
},
error: function(model, response) {
console.error('DELETE failed:', response.status);
}
});
Expected output:
Model destroyed event fired
DELETE successful
Model ID after destroy: null
Model isNew: true
Custom Response Handling
Servers may return data in different formats. Override parse to transform responses.
var Task = Backbone.Model.extend({
urlRoot: '/api/tasks',
// Transform server response
parse: function(response) {
// Server wraps data in {data: {...}}
return response.data || response;
}
});
var TaskList = Backbone.Collection.extend({
model: Task,
url: '/api/tasks',
parse: function(response) {
// Server sends {tasks: [...], total: 100}
return response.tasks || response;
}
});
// The parse method runs automatically during fetch/save
console.log('parse transforms server data into model attributes');
Custom Headers and Authentication
Pass custom headers through the beforeSend callback or override sync.
var SecureModel = Backbone.Model.extend({
urlRoot: '/api/secure',
// Method 1: beforeSend in each call
fetch: function(options) {
options = options || {};
options.beforeSend = function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem('token'));
};
return Backbone.Model.prototype.fetch.call(this, options);
}
});
// Method 2: Override sync globally for auth headers
var originalSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
options.headers = {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'X-API-Version': '2.0'
};
return originalSync.call(this, method, model, options);
};
var model = new SecureModel({ id: 1 });
model.fetch({
success: function() {
console.log('Fetched with auth headers');
}
});
Common Mistakes
- Not setting
urlRootorurl. Without a URL,save()andfetch()throw errors. Always configure URLs on Models and Collections. - Assuming the server returns the same format as the model. Use
parse()to transform server responses into model attributes. - Not handling server errors. Network failures and 500 errors are silent if no error callback is provided. Always handle both success and error.
- Calling
save()without waiting for the response.save()is async. Chaining code aftersave()runs before the server responds. Use the success callback. - Using the wrong HTTP method for the operation. Backbone auto-selects the method based on model state. Verify with
model.isNew()if unsure.
Practice Questions
- What HTTP method does
save()use for a new model? - How does Backbone determine the URL for a model?
- What is the
parsemethod used for? - How do you send only changed attributes in an update?
- Challenge: Create a Model that communicates with an API that returns data wrapped in
{status: "ok", data: {...}}. Overrideparse()andtoJSON()to handle this format.
FAQ
Mini Project
Create a complete CRUD interface for a Customer Model with firstName, lastName, email, and phone attributes. Implement all four operations against a REST API. Include error handling and loading states. Use parse() to unwrap server responses.
What's Next
Now that you understand REST persistence, learn Backbone Marionette Introduction for building larger applications. Then explore Backbone Testing for testing strategies.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro