Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created June 28, 2018 16:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/f43fc90eec99db4d767de0cd3fade4f9 to your computer and use it in GitHub Desktop.
Save JoshCheek/f43fc90eec99db4d767de0cd3fade4f9 to your computer and use it in GitHub Desktop.
Complex JSON API Example
# SETUP DB
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
# SCHEMA
ActiveRecord::Schema.define do
self.verbose = false
create_table :users do |t|
t.string :name
t.timestamps
end
create_table :posts do |t|
t.string :body
t.integer :author_id
t.timestamps
end
create_table :comments do |t|
t.string :body
t.integer :author_id
t.integer :post_id
t.timestamps
end
end
# MODELS
class User < ActiveRecord::Base
has_many :posts, foreign_key: :author
has_many :comments, foreign_key: :author
end
class Post < ActiveRecord::Base
belongs_to :author, class_name: 'User'
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :author, class_name: 'User'
end
# DATA
josh = User.create! name: 'Josh'
yumin = User.create! name: 'Yumin'
gomez = User.create! name: 'Gomez'
Post.create! author: gomez, body: 'first' do |post|
post.comments.build author: josh, body: "first"
end
Post.create! author: josh, body: 'yo ho ho' do |post|
post.comments.build author: yumin, body: "yo yo ma"
post.comments.build author: gomez, body: "yo mama"
end
Post.create! author: josh, body: 'and a bottle of rum'
# JSON API RESOURCES
require 'jsonapi/serializable'
class SerializablePost < JSONAPI::Serializable::Resource
type 'post'
attributes :body
attribute(:date) { @object.created_at }
belongs_to :author
has_many :comments do
data { @object.comments }
link(:all) { @url_helpers.comment_posts_url(@object.id) }
meta { { count: @object.comments.count } }
end
link(:self) { @url_helpers.post_url(@object.id) }
meta { { featured: true } }
end
class SerializableComment < JSONAPI::Serializable::Resource
type 'comment'
attributes :body
attribute(:date) { @object.created_at }
belongs_to :author
belongs_to :post
link(:self) { @url_helpers.comment_url(@object.id) }
end
class SerializableUser < JSONAPI::Serializable::Resource
type 'user'
attributes :name
attribute(:joined) { @object.created_at }
has_many :posts
has_many :comments
link(:self) { @url_helpers.user_url(@object.id) }
end
# THEIR HELPERS
class MyUrlHelper
def post_url(id) "/posts/#{id}" end
def user_posts_url(id) "/posts?user=#{id}" end
def comment_posts_url(id) "/comments?post_id=#{id}" end
def comment_url(id) "/comments/#{id}" end
def user_url(id) "/users/#{id}" end
end
# RENDER A POST TO JSON (feels weird that it returns an array in included)
renderer = JSONAPI::Serializable::Renderer.new
renderer.render(
Post.first,
class: { User: SerializableUser, Comment: SerializableComment, Post: SerializablePost },
expose: { url_helpers: MyUrlHelper.new },
include: [:author, comments: [:author]],
fields: { users: [:name, :email] },
)
# => {:data=>
# {:id=>"1",
# :type=>:post,
# :attributes=>{:body=>"first", :date=>2018-06-28 16:07:23 UTC},
# :relationships=>
# {:author=>{:data=>{:type=>:user, :id=>"3"}},
# :comments=>
# {:links=>{:all=>"/comments?post_id=1"},
# :data=>[{:type=>:comment, :id=>"1"}],
# :meta=>{:count=>1}}},
# :links=>{:self=>"/posts/1"},
# :meta=>{:featured=>true}},
# :included=>
# [{:id=>"3",
# :type=>:user,
# :attributes=>{:name=>"Gomez", :joined=>2018-06-28 16:07:23 UTC},
# :relationships=>
# {:posts=>{:meta=>{:included=>false}},
# :comments=>{:meta=>{:included=>false}}},
# :links=>{:self=>"/users/3"}},
# {:id=>"1",
# :type=>:comment,
# :attributes=>{:body=>"first", :date=>2018-06-28 16:07:23 UTC},
# :relationships=>
# {:author=>{:data=>{:type=>:user, :id=>"1"}},
# :post=>{:meta=>{:included=>false}}},
# :links=>{:self=>"/comments/1"}},
# {:id=>"1",
# :type=>:user,
# :attributes=>{:name=>"Josh", :joined=>2018-06-28 16:07:23 UTC},
# :relationships=>
# {:posts=>{:meta=>{:included=>false}},
# :comments=>{:meta=>{:included=>false}}},
# :links=>{:self=>"/users/1"}}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment