mvbl fst
mvbl fst

Reputation: 5263

Set up REST routes in Express JS for Ajax only to use with Backbone

I am working on re-writing an existing web site using Node.js with Express.

Front-end of the site will use Backbone JS and thus I need to have all necessary routes comply with native Backbone sync. Now most of URL's the client and for Backbone sync will be same. But they won't work for regular GET as they would need to return JSON.

So I am thinking, would it be a good idea to add extension to Model/Collection URLs in Backbone, such as .json, and in Express to have this for every route:

app.get('/p/:topCategory/:category/:product.:format', function(req, res) { ... });

Where if (req.params.id == 'json') than we send JSON, otherwise we render HTML?

Or is there a better approach? Please help.

Upvotes: 7

Views: 6777

Answers (4)

Joey Guerra
Joey Guerra

Reputation: 596

I think the correct way to do this is to implement content negotiation into your application. Yeah, Express 3.x facility is a way to do it and provides a direct answer to your question, but I don't think it's the best way to do it because it places the content negotiation responsibility in the routing logic. I don't think that's a good place for it because it doesn't follow the single responsibility principle, having put the content negotiation in routing logic.

I've taken a stab at implementing content negotiation in my blog engine; reviewing that might help guide you in a good direction. The gist is that the code determines the file extension via content negotiation logic. Then, with the file extension, it wants to find the corresponding view file, renders it into a response and sends it back to the client. The idea is that it responds with the requested resource in the requested representation per the content negotiation. The routing logic only specifies a view, but has no idea about content negotiation. That happens outside of the routing logic which makes for a more flexible design.

The result of this design is the ability to ask for a specific representation of the resource like:

http://blog.joeyguerra.com/index.json and get a JSON representation http://blog.joeyguerra.com/index.phtml and get partial (or an HTML fragment) HTML representation http://blog.joeyguerra.com/index.xml and get an XML representation.

Upvotes: 2

techjacker
techjacker

Reputation: 1438

An alternative that also checks that the "X-Requested-With" header is set for jQuery et al.

var onlyAllowJsonRequests = function (req, res, next) {

    var acceptJson = (req.accepted.length && _.any(req.accepted, function (acc) { return acc.value.indexOf("json") !== -1 }));

    // also check that "X-Requested-With": "XMLHttpRequest" header is set
    if (acceptJson && (req.xhr === true)) {
        next();
    } else {
        res.send(406, "Not Acceptable");
    }
};

app.use(onlyAllowJsonRequests);

NB underscore is a depency.

Upvotes: 2

alessioalex
alessioalex

Reputation: 63663

The better way of doing this would be to use the content negotiation feature in Express 3.x, namely res.format:

https://github.com/visionmedia/express/blob/master/lib/response.js#L299-378

res.format({
  text: function(){
    res.send('hey');
  },

  html: function(){
    res.send('<p>hey</p>');
  },

  json: function(){
    res.send({ message: 'hey' });
  }
});

You approach is also ok, Yammer for ex. is using the same approach: http://developer.yammer.com/api/#message-viewing

Upvotes: 12

ebohlman
ebohlman

Reputation: 15003

Use Accept headers in your requests: Accept: application/json if you want to receive JSON, Accept: text/HTML if you want HTML.

Upvotes: 6

Related Questions