backbone-to-sails
Backbone SDK for communicating with Sails.js over Socket.io.
Background
This has been a long time coming.
Our team has been using this kind of setup with Sails on top of Backbone for almost two years, and I've made plenty of mistakes along the way. So this time, with the first open-source version, I wanted to start small-- keep it simple and clean, without touching more of the Backbone core than absolutely necessary.
So currently, this SDK just replaces the default Backbone.sync function with a Sails.js-flavored socket.io transport. That may change in the future, but any extensions to this functionality will be configurable and opt-in.
Installation
Copy sails.io.backbone.js
into your project and set it up to load in your HTML file.
It should be included after jQuery
, _
, backbone
, socket.io.js
, and sails.io.js
.
That's it!
Usage
Sending messages to the server (i.e. ajax)
There is no special usage for sending requests-- everything works just like you would normally with Backbone via HTTP.
Just be sure and wait for the socket to be connected first! Otherwise you'll see an ugly, but hopefully descriptive) error message.
For example:
socket.on('connect', function socketReady() {
var Twinkies = Backbone.Collection.extend({ url: '/twinkie '});
var someTwinkies = new Twinkies();
someTwinkies.fetch();
someTwinkies.on('sync', function () {
var myTwinkie = someTwinkies.get(1);
myTwinkie.wasEaten = true;
myTwinkie.save();
if (someTwinkies.length > 5) {
var suspiciousTwinkie = someTwinkies.get(2);
suspiciousTwinkie.destroy();
}
});
})
Receiving messages from the server (i.e. comet)
This SDK adds a comet
event to the global Backbone
object:
Backbone.on('comet', function ( message ) {
})`
Using Basic Pubsub with Sails / Backbone
In Sails, subscription happens on the server. This is the only way to securely implement realtime updates.
In order for your socket to receive published messages at all, you must first send a request using the socket to an endpoint (i.e. route, url, pick your favorite term) which subscribes to one or more model instances.
If you're new to pubsub in Sails, or for a refresher on server-side usage,
check out the docs on the Sails website:
http://sailsjs.com/#!documentation/sockets
And the screencasts here:
http://irlnathan.github.io/sailscasts/blog/2013/09/15/episode-20-adding-real-time-events-to-models-in-4-lines-of-code/
Let's say we have a little something-something on the backend reachable at GET /twinkie/subscribe
.
On the server, GET /twinkie/subscribe
might look like:
subscribe: function (req, res) {
Twinkie.subscribe(req.socket, req.param('id'));
res.json({ success: true });
}
On the client, we'll send a request to the endpoint which subscribes us. (using the socket!)
socket.get('/user/subscribe', { id: 7 }, function (response) {
Backbone.on('comet', function ( message ) {
console.log('Got the latest on our twinkie:', message);
});
});
Even Easier-- Backbone + Comet + Sails Blueprints
The built-in API blueprints in Sails automatically manage publish and subscribe on the server for you. All you have to do to subscribe to a model is call the find
blueprint. I'll extend our twinkie example:
var Twinkies = Backbone.Collection.extend({ url: '/twinkie '});
var someTwinkies = new Twinkies();
socket.on('connect', function socketReady() {
someTwinkies.fetch();
someTwinkies.on('sync', function () {
var myTwinkie = someTwinkies.get(1);
myTwinkie.wasEaten = true;
myTwinkie.save();
if (someTwinkies.length > 5) {
var suspiciousTwinkie = someTwinkies.get(2);
suspiciousTwinkie.destroy();
}
});
});
Backbone.on('comet', function ( message ) {
if ( message.model !== 'twinkie' ) {
console.error(
'Unrecognized comet message received from server-- ' +
'I only know how to handle Twinkies!!!'
);
}
switch (message.method) {
case 'create': Twinkies.add(message.data); break;
case 'update': Twinkies.get(message.id).set(message.changes); break;
case 'destroy': Twinkies.remove(Twinkes.get(message.id)); break;
}
})`
How does this SDK work?
Connecting
i.e. How does this connect to the Sails server?
Currently, this SDK expects active socket to be located at
window.socket
, Backbone.socket
or the socket
property on
the instance of the specific model/collection communicating w/
the server. See inline comments if you want to change it.
Talking to the Server
i.e. How does this send messages to the Sails server?
This client will emit socket requests to URLs, which will be
interpreted and routed by Sails accordingly, whether they are
to custom URLs in your routes.js file or automatic API blueprints.
e.g. if your Backbone collection's URL is '/todo', calling fetch()
will still contact the Sails backend at GET /todo
-- but now it will
use Socket.io to emit a packet on the connected socket instead of
sending an HTTP request.
Listening to the Server
i.e. What happens when comet messages arrive?
When your Sails publishes a message using Foo.publish
, the name of
the socket event is always 'message'. This SDK examines all incoming
messages from Sails, then triggers a comet
event on the Backbone
global.
Roadmap
-
Build sails.io.$.js
as a replacement for sails.io.js
and make it a dependency of this library. This allows us to do a better job at simulating an XHR object, and should make it possible for other folks to rapidly integrate other jQuery-dependent frameworks as needed.
-
Built-in auto-synchronization for Backbone.Collection and Backbone.Models, accomplished by expecting a standard CRUD-compatible format in the published messages from the server. Figuring out that format, and making it reasonably configurable, is the real challenge here.
-
Ability to pass in a connected socket as an option to fetch/save/etc. as an alternative to stuffing it in a conventional place (i.e. window.socket
, Backbone.socket
, etc.)
-
If Backbone tries to talk to the server and the socket is missing or disconnected, fall back to $.ajax
. Should be configurable, with at least three distinct possibilities:
- Throw a fatal error .
- Fall back to
$.ajax
, but log a warning. - Fall back to
$.ajax
silently (production)
-
Somewhere (but probably in the aforementioned sails.io.$.js
or sails.io.js
) create a queue which tracks requests intended for the server that were sent before the socket connects. If the queue gets too large, or the socket hasn't connected after 10-15 seconds, give up and release the simulated XHR objects with errors explaining the situation. This lets us ignore connection/disconnection logic, which should dramatically simplify things for beginners.
License
MIT bro