RESTful Backbone.js Implementation using Slim API
There are lots of great resources out on the web for Backbone.js. You can find lots of different tutorials discussing Backbone.js Models, Backbone.js Collections and Backbone.js Views. Backbone.js is a favorite of the JavaScript community for building rich and well-organized client-side applications in JavaScript.
However, eventually there comes a point where all of this neat stuff you are doing on the client-side will have to be persisted by saving your data to a server somewhere. After you do this, you’ll of course need a way to retrieve it later. Otherwise, every time a user refreshes their browser or closes their browser and comes back to your page, anything that they’d done with your app previously will be gone.
If you’re doing any sort of retrieving and/or saving models from the database, you’ll likely be using the fetch, save, destroy, and/or sync methods (or similar implementations). As the documentation on these methods indicate, these methods send asynchronous GET, POST, PUT, and DELETE requests to a URL (usually the one specified in the urlRoot property on a model or a collection). What we need to do to hook Backbone up with a server so we can consume these requests is to use an API (Application Programming Interface). This API will provide us with server-sided code that can respond to the requests coming from Backbone. Which API we use doesn’t matter. Actually I think one of the great things about client sided JavaScript applications is that you can hook up to any server/API that you want. The important thing is that we know how to set up the API properly and write code to create the URLs Backbone is sending requests to and then handle those requests on the server side.
In what follows, we’re going to be using the Slim Framework API which describes itself as…
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
Since it’s a PHP based API we’ll be running it on WAMP server. A video on how to get WAMP server setup can be found here. But if you have another API that you like to use you can use that if you prefer.
So to start, let’s create some endpoints in our API.
Server Side: PHP & Slim API
The Slim Framework Documentation website gives some good info on everything you need to know about how to use the API. But a lot of that is overkill for right now. We’ll just modify the simple example that’s included with the Slim installation package/download. At the time of this writing I am using Slim version 2.2. I should probably also mention that PHP 5.3 is required to run this version of the Slim Framework (so if you’re trying to run it on a server with PHP 5.2 or below, it will not work.
Following the example there we can modify the sample GET, POST, PUT, and DELETE endpoints in the index.php. In this demonstration we’re going to be working with musical artists just as a hypothetical application example, so following RESTful convention our endpoint is going to be “/artist”. Recall that the REST pattern is resource based so the endpoints will know what to do depending on the HTTP verbs that get passed along with the request (i.e. GET, PUT, POST, DELETE). What’s nice about this is that there will only be one URL (excluding parameters) to keep track of for saving and fetching our “Artist” models.
For this exercise I created a new directory and extracted the Slim directory to it and renamed the directory “api”. So now let’s create some endpoints in our Slim API for the artists by modifying index.php file in the Slim package…
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
// GET route with parameter
$app->get('/artist', function () use ($app) {
// get all artists
});
// GET route with parameter
$app->get('/artist/:id', function () use ($app) {
$request = (array) json_decode($app->request()->getBody());
// use $request['id'] to query database based on id and create response...
$response['id'] = 1;
$response['name'] = "Mike Jones";
$response['favoriteColor'] = "blue";
$response['favoriteFood'] = "tacos";
$app->response()->header('Content-Type', 'application/json');
echo json_encode($response);
});
// POST route
$app->post('/artist', function () use ($app) {
$request = (array) json_decode($app->request()->getBody());
// Save to database here creating a new entry with $request['name']; $request['favoriteColor']; etc.
$app->response()->header('Content-Type', 'application/json');
echo json_encode($request);
});
// PUT route
$app->put('/artist/:id', function () use ($app) {
$request = (array) json_decode($app->request()->getBody());
// use $request['id'] to update database based on id and create response...
$app->response()->header('Content-Type', 'application/json');
echo json_encode($request);
});
// DELETE route
$app->delete('/artist/:id', function () use ($app) {
$request = (array) json_decode($app->request()->getBody());
//use $request['id'] to remove database entry based on id...
$app->response()->header('Content-Type', 'application/json');
echo json_encode($request);
});
$app->run();
So let’s discuss what we’ve done here. We’ve created a bunch of routes (or endpoints) in our API marked with the GET, POST, PUT, and DELETE comments. We are keeping the code inside each of these routes to a minimum as the purpose is merely to illustrate that we can both consume JSON coming from Backbone.js (which we will write in the next section) and return JSON from the server — getting that round trip. For the moment, we’re not going to worry about creating a database. In a real application, inside each of these routes you would call the classes and functions to create a database connection and run whatever SQL you wanted to depending on what type of request was made and how you are storing your data. But if you can get and return data to and from your API, you’re in good shape, at least to start.
The GET request without a parameter would normally be used to get an entire list (or collection) of artists. We won’t go into detail about that on this occasion since we’re just going to be dealing with individual models.
If we call fetch on a Backbone model and we specify an id, then a GET request with an id parameter will be called and then API will return data based on the ID that was passed in. The response in this demonstration is simulated (again, normally you’d retrieve this from the database), but it’s there to illustrate the concept.
If we call the save method on a Backbone model and there is no id present on the model then a POST request will be sent to the server. If our model does have an id set, then a PUT request (i.e. an update) will be sent to the server. We can see the 2 different endpoints in the code to handle each case.
Finally, if we call destroy on a model passing the id of the model up to the server, the model with the id specified will be deleted.
Now that we know what our endpoints should do (at least hypothetically), we can write some Backbone code to hit them.
HTML
The markup we’ll be using is shown below…
<!DOCTYPE html> <html> <head> <title>Backbone Application</title> <script src="js/jquery.js" type="text/javascript"></script> <script src="js/underscore.js" type="text/javascript"></script> <script src="js/backbone.js" type="text/javascript"></script> </head> <body> <div id="page"></div> <button id="add">Add</button> <button id="fetch">Fetch</button> <button id="save">Save</button> <button id="destroy">Destroy</button> <script type="text/javascript"> </script> </body> </html>
We’ll be adding our Backbone code in the <script></script> block. Again, this isn’t exactly something you’d want to do if you were developing a production-grade Backbone application (you’d want your code in separate JavaScript files), but for this example, it’ll do nicely.
Client Side: Backbone.js
Our Backbone code will be pretty straightforward. We’ll create our artist model and attach some event handlers to some buttons using jQuery. First though, we’ll define the url that we’ll be using for our urlRoot property on the model. I’m only doing things this way so that this can be dropped anywhere in any directory of a WAMP install. In a production application, you’d want to set your actual URL in a config file somewhere. So I wouldn’t recommend doing the following in production as it’s a bit messy and there are better ways to do things…
var currentURL = "<?php echo $_SERVER['REQUEST_URI']; ?>api/";
Recall before that when setting this project up we created a new directory and extracted the Slim directory to it and renamed the directory “api”. That’s why we’ve done things like this in this setup. Now we’ll create our model using setting the urlRoot to what we set in the previous step plus the ‘artist’ path…
var Artist = Backbone.Model.extend({
initialize: function(){
console.log('Artist has been created...');
this.on("change:name",function(){
console.log('Artist has been changed. Name: ' + this.get('name') + ' ');
});
},
defaults: {
name: "New Artist",
favoriteColor: "gray",
favoriteFood: "Chicken"
},
urlRoot: currentURL + 'artist'
});
Which will resolve our urlRoot for our model to something like http://localhost/[name of your app folder]/api/artist. We are also listening for changes to our model, which will happen with various calls we make to our API. We’ll need to set a currentID just to have a value to plug in to the id property of our models. Let’s also create a function that will just allow us to show that the model is being updated as we click on the various buttons. We’ll call this function on every successful return from the server…
var currentID = 1;
function printData(name, color, food){
$('#page').html('Current artist is... ' + name + ' Favorite color...' + color + ' Favorite food...' + food);
}
Now we can create a new instance of our model…
var artist = new Artist({ id: currentID });
printData(artist.get("name"), artist.get("favoriteColor"), artist.get("favoriteFood"));
Now let’s attach some event handlers to our HTML buttons. In an effort to keep the amount of code that’s somewhat outside the scope of this exercise to a minimum, we’ll just put things inside of a jQuery click event. Most of the time in your Backbone applications, you’ll want to have your buttons and events attached to Backbone Views. But here we will add one event to each button (adding return false at the end to prevent a postback that a button normally creates)…
// Add -> POST
$('#add').click(function(){
var newArtist = new Artist();
newArtist.save({
success: function(model, response, options) {
currentID++;
model.set('id', currentID);
printData(model.get("name"), model.get("favoriteColor"), model.get("favoriteFood"));
},
error: function(model, xhr, options) {
console.log('An error occured while saving the data...');
}
});
return false;
});
// Fetch -> GET /:id
$('#fetch').click(function(){
artist.fetch({
wait: true,
success: function(model, response, options) {
currentID = model.get('id');
printData(model.get("name"), model.get("favoriteColor"), model.get("favoriteFood"));
},
error: function(model, response, options) {
console.log('An error occured while fetching the data...');
}
});
return false;
});
// Save -> PUT /:id
$('#save').click(function(){
currentID++;
artist.save({id: currentID, name: 'Taylor Swift', favoriteColor: 'Pink', favoriteFood: 'Pizza'},{
wait: true,
success: function(model, response, options){
printData(model.get("name"), model.get("favoriteColor"), model.get("favoriteFood"));
},
error: function(model, xhr, options) {
console.log('An error occured while saving the data...');
}
});
return false;
});
// Destroy -> DELETE /:id
$('#destroy').click(function(){
artist.destroy({
wait: true,
success: function(model, response, options){
model.clear();
currentID = 1;
printData(model.get("name"), model.get("favoriteColor"), model.get("favoriteFood"));
},
error: function(model, xhr, options) {
console.log('An error occured while saving the data...');
}
});
return false;
});
NOTE: When we send requests to the various routes, what we do in the success function (after a successful request) is fairly arbitrary since we’re not really actually saving anything to a server.
Notice on some of the calls, we pass in {wait: true} so that our change event doesn’t fire twice. As the documentation for Backbone.js says…
Calling save with new attributes will cause a “change” event immediately, a “request” event as the Ajax request begins to go to the server, and a “sync” event after the server has acknowledged the successful change. Pass {wait: true} if you’d like to wait for the server before setting the new attributes on the model.
There may be some instances where you wanted to respond the change event in both places, but if not this is the way around that.
So here we can see inside each of these buttons’ events we are calling the different models. Data is going up to the server and data (albeit arbitrary fake data) is being returned. We’re getting the round trip that we want when developing a Backbone application with a RESTful API. To take things further you’d then need to create a database and in the PHP code connect to it and run the queries and return whatever data you want back to your Backbone application. Things are quite open-ended and the solution you put into place is entirely up to you. But this should give a start into how you’d get an API set up to consume your application’s requests. It’s definitely a bit disjointed, but the important thing is that we’re able to hit all the routes.
You can download the example files below to see how everything fits together. Hope you have found this somewhat helpful as guide to a starting point for your Backbone.js applications that save data to a server






5 Responses to RESTful Backbone.js Implementation using Slim API