Slide 1

Slide 1 text

Optimizing An API For Ember Data Dan Gebhardt Updated for BOSTON Ember.js

Slide 2

Slide 2 text

Dan Gebhardt @dgeb

Slide 3

Slide 3 text

Convention Configuration

Slide 4

Slide 4 text

“Trivial choices are the enemy” - Yehuda Katz

Slide 5

Slide 5 text

Ruby on Rails ActiveModel::Serializers Ember.js Ember Data

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

“What” not “how” DRY Customizable ActiveModel::Serializers

Slide 8

Slide 8 text

Ember Data In memory store Canonical records Multi-layered architecture Flexible and customizable

Slide 9

Slide 9 text

Ember Data

Slide 10

Slide 10 text

Ember Data Serializer Adapter Store

Slide 11

Slide 11 text

JSON underscore_naming include root element id: 1, fk_id: 1, fk_ids: [1] conventions for including related data AM::S Conventions

Slide 12

Slide 12 text

DS.RESTAdapter Conventions JSON underscore_naming include root element id: 1, fk_id: 1, fk_ids: [1] conventions for including related data

Slide 13

Slide 13 text

DS.RESTAdapter Conventions JSON underscore_naming include root element id: 1, fk_id: 1, fk_ids: [1] conventions for including related data IDENTICAL TO ActiveModel::Serializers

Slide 14

Slide 14 text

class ApplicationSerializer < ActiveModel::Serializer # sideload related data by default embed :ids, include: true end AM::S Configuration

Slide 15

Slide 15 text

Ember Data Configuration App.Adapter = DS.RESTAdapter.extend(); App.Store = DS.Store.extend({ revision: 12, adapter: 'App.Adapter' });

Slide 16

Slide 16 text

Relationships source: twitamore.com

Slide 17

Slide 17 text

One-to-Many Relationships App.Post = DS.Model.extend({ title: DS.attr('string'), comments: DS.hasMany('App.Comment') }); App.Comment = DS.Model.extend({ body: DS.attr('string'), post: DS.belongsTo('App.Post') });

Slide 18

Slide 18 text

class PostSerializer < ApplicationSerializer attributes :title has_many :comments end class CommentSerializer < ApplicationSerializer attributes :body belongs_to :post end One-to-Many Relationships

Slide 19

Slide 19 text

{ post: { id: 1, title: 'Ember is Omakase', comment_ids: [4, 5, 6] }, comments: [ {id: 4, post_id: 1, body: 'delicious!'}, {id: 5, post_id: 1, body: 'yuno turbolinks?'}, {id: 6, post_id: 1, body: 'is that a tentacle?'} ] } JSON One-to-Many Relationships

Slide 20

Slide 20 text

One-to-One Relationships App.User = DS.Model.extend({ name: DS.attr('string'), rights: DS.belongsTo('App.Rights') }); App.Rights = DS.Model.extend({ admin: DS.attr('boolean'), user: DS.belongsTo('App.User') });

Slide 21

Slide 21 text

class UserSerializer < ApplicationSerializer attributes :id, :name has_one :rights end class RightsSerializer < ApplicationSerializer attributes :id, :admin belongs_to :user end One-to-One Relationships

Slide 22

Slide 22 text

{ user: { id: 1, name: '', rights_id: 2 }, rights: [{ id: 2, admin: true, user_id: 1 }] } JSON One-to-One Relationships

Slide 23

Slide 23 text

One-to-None Relationships App.User = DS.Model.extend({ name: DS.attr('string'), rights: DS.belongsTo('App.Rights') }); App.Rights = DS.Model.extend({ admin: DS.attr('boolean') });

Slide 24

Slide 24 text

class UserSerializer < ApplicationSerializer attributes :id, :name has_one :rights end class RightsSerializer < ApplicationSerializer attributes :id, :admin end One-to-None Relationships

Slide 25

Slide 25 text

{ user: { id: 1, name: '', rights_id: 2 }, rights: [{ id: 2, admin: true }] } JSON One-to-None Relationships

Slide 26

Slide 26 text

Many-to-Many Relationships App.Post = DS.Model.extend({ title: DS.attr('string'), body: DS.attr('string'), tags: DS.hasMany('App.Tag') }); App.Tag = DS.Model.extend({ name: DS.attr('string'), posts: DS.hasMany('App.Post') });

Slide 27

Slide 27 text

class PostSerializer < ApplicationSerializer attributes :id, :title, :body has_many :tags end class TagSerializer < ApplicationSerializer attributes :id, :name has_many :posts end Many-to-Many Relationships

Slide 28

Slide 28 text

{ posts: [ {id: 1, title: 'Hello world', tag_ids: [11, 12]}, {id: 2, title: 'Goodbye', tag_ids: [11, 13]} ], tags: [ {id: 11, name: 'announcements', post_ids: [1,2]}, {id: 12, name: 'happy', post_ids: [1]}, {id: 13, name: 'sad', post_ids: [2]} ] } JSON Many-to-Many Relationships

Slide 29

Slide 29 text

Many-to-None Relationships App.Post = DS.Model.extend({ title: DS.attr('string'), body: DS.attr('string'), tags: DS.hasMany('App.Tag') }); App.Tag = DS.Model.extend({ name: DS.attr('string') });

Slide 30

Slide 30 text

class PostSerializer < ApplicationSerializer attributes :id, :title, :body has_many :tags end class TagSerializer < ApplicationSerializer attributes :id, :name end Many-to-None Relationships

Slide 31

Slide 31 text

{ posts: [ {id: 1, title: 'Hello world', tag_ids: [11, 12]}, {id: 2, title: 'Goodbye', tag_ids: [11, 13]} ], tags: [ {id: 11, name: 'announcements'}, {id: 12, name: 'happy'}, {id: 13, name: 'sad'} ] } JSON Many-to-None Relationships

Slide 32

Slide 32 text

Embedded Relationships Creative Commons licensed by: Subhash Chandra

Slide 33

Slide 33 text

{ post: { id: 1, title: 'Ember is Omakase', comments: [ {id: 4, body: 'delicious!'}, {id: 5, body: 'yuno turbolinks?'}, {id: 6, body: 'is that a tentacle?'} ] } } JSON Serialized Embedded Data

Slide 34

Slide 34 text

class PostSerializer < ApplicationSerializer attributes :title has_many :comments, embed: :objects end class CommentSerializer < ApplicationSerializer attributes :body belongs_to :post end Embedded Data Serializers

Slide 35

Slide 35 text

Embedded Read-only Data App.Post = DS.Model.extend({ title: DS.attr('string'), comments: DS.hasMany('App.Comment') }); App.Comment = DS.Model.extend({ body: DS.attr('string') }); App.Adapter.map('App.Post', { comments: {embedded: 'load'} });

Slide 36

Slide 36 text

Embedded Writeable Data App.Post = DS.Model.extend({ title: DS.attr('string'), comments: DS.hasMany('App.Comment') }); App.Comment = DS.Model.extend({ body: DS.attr('string') }); App.Adapter.map('App.Post', { comments: {embedded: 'always'} });

Slide 37

Slide 37 text

Customizations

Slide 38

Slide 38 text

{ hobbit: {id: 1, name: 'Bilbo'} } { hobbitses: [ {id: 1, name: 'Bilbo'} {id: 2, name: 'Frodo'} {id: 3, name: 'Samwise'} ] } JSON Custom Pluralization

Slide 39

Slide 39 text

Custom Pluralization App.Adapter.configure('plurals', { hobbit: 'hobbitses' });

Slide 40

Slide 40 text

{ post: { id: 1, titleOfPost: 'Ember is Omakase' } } JSON Custom Keys

Slide 41

Slide 41 text

Custom Keys App.Adapter.map('App.Post', { title: {key: 'titleOfPost'} });

Slide 42

Slide 42 text

{ post: { id: 1, title: 'Ember is Omakase', comment_ids: [4, 5, 6] }, post_comments: [ {id: 4, post_id: 1, body: 'delicious!'}, {id: 5, post_id: 1, body: 'yuno turbolinks?'}, {id: 6, post_id: 1, body: 'is that a tentacle?'} ] } JSON Custom Sideloading

Slide 43

Slide 43 text

Custom Sideloading App.Adapter.configure('App.Comment', { sideloadAs: 'post_comments' });

Slide 44

Slide 44 text

Custom Transforms App.Adapter.registerTransform('excitableString', { serialize: function(value) { return value + '!'; }, deserialize: function(value) { return value.substring(0, value.length - 1); } }); App.Greeting = DS.Model.extend({ message: DS.attr('excitableString') });

Slide 45

Slide 45 text

Custom URLs App.Adapter.reopen({namespace: 'ember'}); person = store.find(Person, 1); // => /ember/people/1 App.Adapter.reopen({url: 'http://api.ember.dev'}); person = store.find(Person, 1); // => http://api.ember.dev/people/1

Slide 46

Slide 46 text

Bulk Commits App.Adapter.reopen({bulkCommit: true}); store.createRecord(App.Person, {name: 'tomdale'}); store.createRecord(App.Person, {name: 'wycats'}); store.commit(); // POST to /people // {people: [{name: 'tomdale'}, {name: 'wycats'}]}

Slide 47

Slide 47 text

Edge of Convention Pagination Authentication Sparse fieldsets Custom includes Polymorphism Creative Commons licensed by: _chrisUK

Slide 48

Slide 48 text

Resources http://emberjs.com/guides/models/ https://github.com/rails-api/active_model_serializers https://github.com/dgeb/ember_data_example Questions? http://stackoverflow.com/questions/tagged/ember-data @dgeb