Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conventions required of RESTful JSON API to be usable with Ember

In the Ember Models Introduction, it is mentioned:

Without any configuration, Ember Data can load and save records and their relationships served via a RESTful JSON API, provided it follows certain conventions.

I'm getting started trying to use a token based, RESTful JSON API, which at first glance is not strictly RESTful. Few examples:

  1. Authentication is implemented as GET /api/[email protected]&password=pass
  2. Most of the API's return Status of 200 (response header) even on failure. The returned json contains additional fields success (boolean) and code (int) which indicate if the API failed or passed.
  3. The urls are not based on nouns (models). For example, a typical message edit operation which should conventionally be a POST to a url like /api/message/1/edit is implemented as GET /api/edit_message?id=1&text=new

So, I was wondering if someone could list what these certain conventions are that are mentioned in the docs. This could help me understand if I could use ember-data or not.

like image 386
Code Poet Avatar asked Feb 23 '14 05:02

Code Poet


1 Answers

The short answer is that EmberData is probably not a good fit for your REST API as your REST API isn't really a rest API (it doesn't use HTTP verbs and instead embeds actions as part of the query string).

Why Ember Data Probably Isn't for You

While it may have been a goal of the Ember Data project in the past to support an API such as the one you are describing, more recently, the Ember Data developers have explicitly stated that they do not intend for Ember Data to be used with non-conventional APIs. For example, the BasicAdapter which was meant to "bridge the gap" and allow the use of Ember data with non-conventional REST APIs has been removed.

Here's the actual quote and link to the blog post at Emberjs.com (worth a read):

"Ember Data will now focus on being the best possible library for Ember.js apps to communicate with consistent, conventional APIs." (http://emberjs.com/blog/2013/05/03/ember-data-progress-update.html)

As recommended in that blog post you should check out the following data persistence libraries which might be better suited to your situation:

https://github.com/endlessinc/ember-restless

https://github.com/ebryn/ember-model

Finally, you can always do it yourself with AJAX much like the Discourse folks did http://eviltrout.com/2013/03/23/ember-without-data.html

Authentication

As far as I know Ember Data does not handle application authentication which is what I think you're getting at with your example there. For your authentication system you could look at something like Ember.SimpleAuth (https://github.com/simplabs/ember-simple-auth) which is highly configurable and should work with your authentication mechanism (although it will definitely require writing a custom Authenticator).

Writing a custom Authenticator is pretty straightforward.

What is Ember Data Actually Expecting

I'd recommend reading over this page if you haven't seen it yet: http://emberjs.com/guides/models/the-rest-adapter/

Ember Data is going to use HTTP Verbs to convey intent. So when you make a call to createRecord on one of your models and then save the store, Ember Data will issue an HTTP POST to your REST API. When you try to get a record, Ember will issue a GET request. When you try to delete a record Ember will issue a DELETE request (etc. etc.).

Lets say you have three models that looks like this:

module.exports = App.ShoppingCart = DS.Model.extend({
    user: DS.belongsTo('user'), 
    items: DS.hasMany('item', {async:true}),
    name: attr('string'),
    enabled: attr('boolean')
});

module.exports = App.Item = DS.Model.extend({
    name: attr('string')
});

module.exports = App.User = DS.Model.extend({
   firstName: attr('string')
   lastName: attr('string')
});

When you attempt to load a record using this.store.find('shoppingCart', 1) Ember will make a GET request to the pluralized form of the model name (in this case GET /shoppingCarts/1). Ember has a bunch of rules built in to determine the plural form of a word so for example it knows that the plural of search is searches and not searchs. Once the GET request is made your REST API needs to return the following JSON:

{
  "shoppingCart": {
      "id": 1,
      "name": "Bobs Shopping Cart",
      "user": 1, //this field links to the user with an id of 1
      "enabled": true,
      "items": [
        1,
        2
      ] 
    }
}

If you were doing this.store.find('shoppingCart') then Ember Data will issue a GET /shoppingCarts and expect back an array of shopping cart objects keyed with the plural form of the model name. For example, like this:

{
  "shoppingCarts": [
     {
       "id": 1, //not specified in the model but must be sent by the REST API
       "name": "Bob's Shopping Cart",
       "user": 1, //this field links to the user with an id of 1
       "enabled": true,
       "items": [
         1,
         2
       ] 
     },
     {
       "id": 2, 
       "name": "John's Shopping Cart",
       "user": 2, //this field links to the user with an id of 2
       "enabled": false,
       "items": [
         3,  // these are ids for the item models
         4
       ] 
     }
  ]
}

Note that when you return records from the server you need to include an id field which uniquely identifies the record being returned. The id field is not specified in the model itself. When you create a new record and send data to the server you don't include the id field (as it will be determined server side) but the REST API would then need to return what the id is in the response.

In the example above, if Ember Data has user "1" cached in the store then it will just use that information, otherwise it will make another GET request to to GET /users/1 to retrieve information for user 1. (You can make this more efficient by sideloading records if you want to avoid multiple GET requests).

To sum up, the convention is to use HTTP verbs to convey what action should be taken, the URL that Ember Data will send the request to is based on the pluralized form of the model name that you are querying.

The big caveat

Most of what I wrote above is based on the assumption that you want to use Ember Data "out of the box" without too much customization. Generally speaking I think Ember Data is easiest to work with when you have control over the REST API and can adjust it to conform to Ember Data's opinion of how a JSON based REST API should work. It is possible to change the default behavior of Ember Data but I'm not very experienced with trying to bend Ember Data to fit my API so you might need some input from someone else that has tried to do this.

like image 140
Sarus Avatar answered Oct 06 '22 07:10

Sarus