Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Allow Router to be used server side #2473

Closed
wants to merge 1 commit into
from

Conversation

Projects
None yet
2 participants

Backbone assumes that History is a singleton which is not the case in
non-browser environments like node.js where multiple requests have to
be handled simultaneously. This commit is not an attempt to rethink how
Backbone handles Controller/Dispatcher logic across different
environments but merely an improvement that allows using Router and
History on the server side without breaking the existing api.

Browser (unchanged)

Backbone.history.start({silent: true, pushState: true, hashChange: true});
var router = new Router();
// Shared code
// Run route
router.loadUrl('/posts/123');
// Set browser location to /posts/123
router.navigate('/posts/123');

Server (stubbing location)

var url = require('url');
var Location = function(req, res) {
    _.extend(this, url.parse(req.url), {req: req, res: res});
};
Location.prototype.assign = 
Location.prototype.replace = function(path) {
    this.res.redirect(path);
};

app.get('/home/', function(req, res) {
    var location = new Location(req, res);
    var history = new Backbone.History({location: location});
    var router = new Router({history: history});
    // Shared code
    // Run route
    router.loadUrl('/posts/123');
    // Redirect (302 "Found") to /posts/123
    router.navigate('/posts/123');
});

Let me know your thoughts and if there are any plans on the future direction of this.

Took 2803ms to run 753 tests. 753 passed, 0 failed.
passed 4 tests
Removed hard coded Backbone.history dependency in Router
Backbone assumes that History is a singleton which is not the case in
non-browser environments like node.js where multiple requests have to
be handled simultaneously. This commit is not an attempt to rethink how
Backbone handles Controller/Dispatcher logic across different
environments but a merely an improvement that allows using Router and
History on the server side without breaking the existing api.
Owner

jashkenas commented Apr 12, 2013

Backbone assumes that History is a singleton which is not the case in
non-browser environments like node.js where multiple requests have to
be handled simultaneously.

What? Node is single-threaded. You can have a single router dispatching to as many "simultaneous" asynchronous route handlers as you'd like.

(cute Gravatar, by the way)

@jashkenas jashkenas closed this Apr 12, 2013

Thanks! :D It seems to split the crowds.

Being single threaded doesn't protect you from race conditions when sharing state across async callbacks. (Which you are with a single instance of Backbone.history).

app.get('/*', function(req, res) {
    // OK
    Backbone.history.loadUrl(req.url);

    // Fail!
    wait().done(function() {
        Backbone.history.loadUrl(req.url);
    });

    // Use case: Redirect depending on async data
    model.fetch().done(function() {
        router.navigate(model.get('something'));
    });
});

Now I know I could redirect using node's api - the reason I'd like leave it to Backbone though is that I want to share the code inside app.get client and server side. Stubbing Location on the server with the ability to set a fresh instance of Backbone.History for each request and passing that into Backbone.Router would open up this use case. The browser could push to html5 history without a page refresh while the server uses the same logic to serve up or redirect to the correct page.
I think allowing to set this.history for each instance of Backbone.Router would give a lot more flexibility. (See also documentcloud#2469 (comment))

Maybe I went overboard with my initial commit - Are you open to adding an extra history option to Backbone.Router or are there any concerns I'm missing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment