Description of the expected REST interface, adapted from http://stackoverflow.com/questions/14922623/what-is-the-complete-list-of-expected-json-responses-for-ds-restadapter :
Context | ServerURL | Method | Req. Data | Resp. Data | |
---|---|---|---|---|---|
Get list of all | /users | GET | {"users":[{...},{...}]} | ||
Get one | /users/123 | GET | {"user":{...}} | ||
Create | /users | POST | {"user":{...}} | {"user":{...}} | |
Update | /users/123 | PUT | {"user":{...}} | {"user":{...}} | |
Delete | /users/123 | DELETE | N/A | null | |
Create in bulk | /users | POST | {"users":[{...},{...}]} | {"users":[{...},{...}]} | |
Update in bulk | /users/bulk | PUT | {"users":[{...},{...}]} | {"users":[{...},{...}]} | |
Delete in bulk | /users/bulk | DELETE | {"users":[1,2]} | {"users":[1,2]} |
This is EmberJS's documentation on this:
http://emberjs.com/guides/models/the-rest-adapter/#toc_the-rest-adapter
Ember Gotchas!
- Ember tolerates no unmapped fields in your JSON results! Presence
of these will result in an Ember.assert() error, which in Firefox
produced no logging whatsoever, just a silent failure!
- Example: if your data looks like this {"users":[{"name":"bob","id":3}], "count":1}
and Ember does not know about the 'count' field, JSON response processing will abort
- Example: if your data looks like this {"users":[{"name":"bob","id":3}], "count":1}
- Ember transforms camel-cased model field
names into underscored names when emitting JSON, and expects underscored
names in retreivals:
- e.g. model field 'isCompleted' will be sent in JSON updates as 'is_completed' - and must be sent as 'is_completed' as well
- WORKAROUND: you must explicitly map the attribute in the RESTAdapter - see the EmberJS documentation here
- or see the related Question below for a workaround
- Q: what is this optional 'meta' property we can put into our JSON data? as seen in extractMany()
- A: TBD
- Q: how can we get Ember to quit looking at fields as being underscored
- A: create a mapping in the REST adapter
- see http://emberjs.com/guides/models/the-rest-adapter/#toc_underscored-attribute-names
- OR, override the Serializer class - somewhere in global code, define your subclass
window.Todos.TodoRESTAdapter = DS.RESTSerializer.extend({ keyForAttributeName: function(type, name) { return name; }, keyForBelongsTo: function(type, name) { var key = this.keyForAttributeName(type, name); if (this.embeddedType(type, name)) { return key; } return key + "Id"; }, keyForHasMany: function(type, name) { var key = this.keyForAttributeName(type, name); if (this.embeddedType(type, name)) { return key; } return this.singularize(key) + "Ids"; } });
Todos.Store = DS.Store.extend({ revision: 11, adapter: DS.RESTAdapter.create({ bulkCommit: false, namespace: "todo/api" serializer: Todos.TodoRESTAdapter }) });
- Q: How to do the analagous replacement of underscore() when serializer is serializing back to REST?
- A: Here is how serializing to REST gets done:
- base class DS.Serializer.serialze() is invoked
- calls DS.Serializer().addAttributes() which loops through all attributes
- calls DS.Serializer().addAttribute() which can be overridden
- calls _keyForAttributeName()
- eventually calls keyForAttributeName() in the Serializer
- this is where we override
- calls _keyForAttributeName()
- A: Here is how serializing to REST gets done:
- Q: how should I represent nested data elements?
- A: use foreign key in owning object, and provide a sideloaded list of the related objects, and/or a custom transformation
- see http://emberjs.com/guides/models/the-rest-adapter/#toc_sideloaded-relationships
- findAll: function(store, type, since)
- contains a 'success': callback - set breakpoint here
- calls didFindAll()
- uses a Loader, acquired as DS.loaderFor(store), and a Serializer
- invokes serializer.extractMany(loader, payload, type)
- where payload is your data! and type = model name
- stepping into this puts you into a wrapper - step again into the 'apply()'
- first parses sideloaded data using sideload()
- see this for description of sideloading:
- extractRecordRepresentation()
- getMappingForType()
- if [ should sideload ]
- loader.sideload()
- else
- loader.load()
- store.load()
- this adds a prematerialization entry into a list for later parsing
- somewhere much deeper in the stack, we get to....
- DS.RESTSerializer.keyForAttributeName()
- which calls Ember.String.decamelize()
- this is where underscores are introduced
- store.load()
- loader.load()
- executed in method: pluralize()
- option 1: provide a mapping in definition of the RESTAdapter, as ....
adapter: DS.RESTAdapter.create({ bulkCommit: false,
- option 2: defaults to name + "s"
- maintains a 'stringCache' which JSON string field names are looked up against
- these are given codes like 'st295' where the # is the field uuid
You may want to check out my ember-data-grails project. It eases the process of turning a Grails server into a RESTful backend that Ember.js and Ember-data will interact with into a cinch.
ReplyDeletehttps://github.com/zachriggle/ember-data-grails
Thanks Zach, I will!
DeleteTHIS should be on the ember documentation. Very useful, many thanks for posting this.
ReplyDelete