ams, api, rails and a developer, a love story
TRANSCRIPT
AMS, API, RAILSand a developer
LOVE STORY
API
make the modern web what it is
API
João Moura co-founder at Gioco
@joaomdmoura gioco.pro
AMS, API, RAILS
AMS, API, RAILSand a developer
AMS, API, RAILSand a developer
LOVE STORY
largest city in the Americas
largest city in the Americas
largest city in the Americas
the most populous city of the southern hemisphere
No Background Job
Bad Relationships
No Background Job
Bad Architecture
Bad Relationships
No Background Job
Bad Architecture
Bad Relationships
Indexes Missing
No Background Job
Bad Architecture
No Cache at all
Bad Relationships
Indexes Missing
No Background Job
Open Source
😘Open Source
APIapplication programming interface
APIapplication programming interface
in·ter·face /ˈin(t)ərˌfās/
APIapplication programming interface
in·ter·face /ˈin(t)ərˌfās/
a point where two systems meet and interact.
ONE OF THE GREATEST
assets
ONE OF THE GREATEST
liabilities
Good API
Good APIPerformanceScalabilityReusabilityEvolvability
Documentation Easy to learnEasy to useHard to misuse
An API should do one thing, and do it well.“
Joshua Blonch, Google tech talk, Jan 2007
why Rails
is the best tool!
why Rails
is the best tool!
It’s not
why Rails
is the best tool!
It’s notBut it’s a great one
Performance Scalability Reusability Evolvability Documentation Easy to learn Easy to use Hard to misuse
Performance Scalability Reusability Evolvability Documentation Easy to learn Easy to use Hard to misuse}
Conventions
Performance Scalability Reusability Evolvability Documentation Easy to learn Easy to use Hard to misuse}
RailsConventions
Performance Scalability Reusability Evolvability Documentation Easy to learn Easy to use }
}RailsConventions
Performance Scalability Reusability Evolvability Documentation Easy to learn Easy to use }
Railsis robust
rails-api
Active Model Serializer
rails-apiSubset of a normal Rails application, created for applications that don't require all functionality of a complete Rails application
Active Model Serializer
Active Model Serializer
AMS
Bringing Convention over Configuration to your JSON
AMS
class PostSerializer < ActiveModel::Serializer
attributes :title, :body, :comments_count
def comments_count object.comments.size end
end
class PostSerializer < ActiveModel::Serializer
class PostSerializer < ActiveModel::Serializer
attributes :title, :body, :comments_count
class PostSerializer < ActiveModel::Serializer
attributes :title, :body, :comments_count
def comments_count object.comments.size end
end
{ id: 1, title: "ZOMG an amazing post", body: "Indeed, my friend", comments_count: 5 }
AMS 0.8.x
AMS 0.9.x
AMS 0.10
1. Adapters Pattern
1. Adapters PatternAdapters describe how attributes and relationships should be serialized.
{ "links": { "self": "http://example.com/posts",
"next": "http://example.com/posts?page[offset]=2",
"last": "http://example.com/posts?page[offset]=10"
}, "data": [{ "type": "posts", "id": "1", "title": "JSON API paints my bikeshed!",
"links": { "self": "http://example.com/posts/1",
"author": { "self": "http://example.com/posts/1/links/author",
"related": "http://example.com/posts/1/author",
"linkage": { "type": "people", "id": "9" }
}, "comments": { "self": "http://example.com/posts/1/links/comments",
"related": "http://example.com/posts/1/comments",
"linkage": [ { "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
] } } }], "included": [{ "type": "people", "id": "9", "first-name": "Dan", "last-name": "Gebhardt",
"twitter": "dgeb", "links": { "self": "http://example.com/people/9"
} }, { "type": "comments", "id": "5", "body": "First!", "links": { "self": "http://example.com/comments/5"
} }, { "type": "comments", "id": "12", "body": "I like XML better",
"links": { "self": "http://example.com/comments/12"
} }] }
2. JSONAPI
{ "links": { "self": "http://example.com/posts",
"next": "http://example.com/posts?page[offset]=2",
"last": "http://example.com/posts?page[offset]=10"
}, "data": [{ "type": "posts", "id": "1", "title": "JSON API paints my bikeshed!",
"links": { "self": "http://example.com/posts/1",
"author": { "self": "http://example.com/posts/1/links/author",
"related": "http://example.com/posts/1/author",
"linkage": { "type": "people", "id": "9" }
}, "comments": { "self": "http://example.com/posts/1/links/comments",
"related": "http://example.com/posts/1/comments",
"linkage": [ { "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
] } } }], "included": [{ "type": "people", "id": "9", "first-name": "Dan", "last-name": "Gebhardt",
"twitter": "dgeb", "links": { "self": "http://example.com/people/9"
} }, { "type": "comments", "id": "5", "body": "First!", "links": { "self": "http://example.com/comments/5"
} }, { "type": "comments", "id": "12", "body": "I like XML better",
"links": { "self": "http://example.com/comments/12"
} }] }
2. JSONAPIA standard for building APIs in JSON.
{ "links": { "self": "http://example.com/posts",
"next": "http://example.com/posts?page[offset]=2",
"last": "http://example.com/posts?page[offset]=10"
}, "data": [{ "type": "posts", "id": "1", "title": "JSON API paints my bikeshed!",
"links": { "self": "http://example.com/posts/1",
"author": { "self": "http://example.com/posts/1/links/author",
"related": "http://example.com/posts/1/author",
"linkage": { "type": "people", "id": "9" }
}, "comments": { "self": "http://example.com/posts/1/links/comments",
"related": "http://example.com/posts/1/comments",
"linkage": [ { "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
] } } }], "included": [{ "type": "people", "id": "9", "first-name": "Dan", "last-name": "Gebhardt",
"twitter": "dgeb", "links": { "self": "http://example.com/people/9"
} }, { "type": "comments", "id": "5", "body": "First!", "links": { "self": "http://example.com/comments/5"
} }, { "type": "comments", "id": "12", "body": "I like XML better",
"links": { "self": "http://example.com/comments/12"
} }] }
2. JSONAPIA standard for building APIs in JSON.
{ "links": { "self": "http://example.com/posts",
"next": "http://example.com/posts?page[offset]=2",
"last": "http://example.com/posts?page[offset]=10"
}, "data": [{ "type": "posts", "id": "1", "title": "JSON API paints my bikeshed!",
"links": { "self": "http://example.com/posts/1",
"author": { "self": "http://example.com/posts/1/links/author",
"related": "http://example.com/posts/1/author",
"linkage": { "type": "people", "id": "9" }
}, "comments": { "self": "http://example.com/posts/1/links/comments",
"related": "http://example.com/posts/1/comments",
"linkage": [ { "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
] } } }], "included": [{ "type": "people", "id": "9", "first-name": "Dan", "last-name": "Gebhardt",
"twitter": "dgeb", "links": { "self": "http://example.com/people/9"
} }, { "type": "comments", "id": "5", "body": "First!", "links": { "self": "http://example.com/comments/5"
} }, { "type": "comments", "id": "12", "body": "I like XML better",
"links": { "self": "http://example.com/comments/12"
} }] }
JSONAPI RC3
JSONAPI 1.0
JSONAPI 1.0on May 21, 2015
3. Cache
3. CacheNew implementation, optimised and following Rails conventions
class PostSerializer < ActiveModel::Serializer
attributes :title, :body end
class PostSerializer < ActiveModel::Serializer cache attributes :title, :body end
class PostSerializer < ActiveModel::Serializer cache key: 'post' attributes :title, :body end
class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 3.hours attributes :title, :body end
class PostsController < ApplicationController
def index @posts = Post.all render json: @posts end
end
class PostsController < ApplicationController
def index @posts = Post.all render json: @posts end
end
class PostsController < ApplicationController
def index @posts = Post.all render json: @posts end
def show @post = Post.find(params[:id]) render json: @post end
end
4. Fragment Cache
4. Fragment CacheRails conventions to cache specific attributes
class PostSerializer < ActiveModel::Serializer
attributes :title, :body, :comments_count def title "Post - #{object.title}" end
def comments_count object.comments.size end
end
def title "Post - #{object.title}" end
def comments_count object.comments.size end
def title "Post - #{object.title}" end
def comments_count object.comments.size end
cacheable
def title "Post - #{object.title}" end
def comments_count object.comments.size end
cacheable
non-cacheable
class PostSerializer < ActiveModel::Serializer
attributes :title, :body, :comments_count def title "Post - #{object.title}" end
def comments_count object.comments.size end
end
class PostSerializer < ActiveModel::Serializer cache only: [:title] attributes :title, :body, :comments_count def title "Post - #{object.title}" end
def comments_count object.comments.size end
end
class PostSerializer < ActiveModel::Serializer cache except: [:comments_count] attributes :title, :body, :comments_count def title "Post - #{object.title}" end
def comments_count object.comments.size end
end
class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 3.hours, only: [:title] attributes :title, :body end
In god we trust…all others bring data
0.9.x42.680000user
system
total
real
17.630000
60.310000
71.201321
0.9.x 0.10.0real 71.201321 63.926189
0.9.x 0.10.0real 71.201321 57.851192
WIP
WIPfetch multibenchmarkand more…
one more thing
AMS 0.10.0
another thing
Rails 5maybe?
special tks!
special tks!
João Moura co-founder at Gioco
@joaomdmoura gioco.pro