ActiveRecord Associations
JSONAPI Suite comes with an ActiveRecord adapter. Though other
adapters can mimic this same interface, here’s what you’ll get
out-of-the-box. The SQL here is roughly the same as using #includes.
Note: make sure to whitelist associations in your serializers or nothing will render!
has_many
/posts?include=comments# app/resources/post_resource.rb
has_many :comments,
scope: -> { Comment.all },
resource: CommentResource,
foreign_key: :post_idbelongs_to
/comments?include=posts# app/resources/comment_resource.rb
belongs_to :post,
scope: -> { Post.all },
resource: PostResource,
foreign_key: :post_idhas_one
/posts?include=detail# app/resources/post_resource.rb
has_one :detail,
scope: -> { PostDetail.all },
resource: PostDetailResource,
foreign_key: :post_idhas_and_belongs_to_many
/posts?include=tags# app/resources/post_resource.rb
has_and_belongs_to_many :tags,
scope: -> { Tag.all },
resource: TagResource,
foreign_key: { taggings: :tag_id }The only difference here is the foreign_key - we’re passing a hash instead of a symbol. taggings is our join table, and tag_id is the true foreign key.
This will work, and for simple many-to-many relationships you can move on. But what if we want to add the property primary, a boolean, to the taggings table? Since we hid this relationship from the API, how will clients access it?
As this is metadata about the relationship it should go on the meta section of the corresponding relationship object. While supporting such an approach is on the JSONAPI Suite roadmap, we haven’t done so yet.
For now, it might be best to simply expose the intermediate table to the API. Using a client like JSORM, the overhead of this approach is minimal.
polymorphic_belongs_to
# app/models/employee.rb
belongs_to :workspace, polymorphic: true# app/models/workspace.rb
has_many :employees, as: :workspace# app/resources/employee_resource.rb
polymorphic_belongs_to :workspace,
group_by: :workspace_type,
groups: {
'Office' => {
scope: -> { Office.all },
resource: OfficeResource,
foreign_key: :workspace_id
},
'HomeOffice' => {
scope: -> { HomeOffice.all },
resource: HomeOfficeResource,
foreign_key: :workspace_id
}
}/employees?include=workspaceHere an Employee belongs to a Workspace. Workspaces have
different types - HomeOffice, Office, CoworkingSpace, etc. The
employees table has workspace_id and workspace_type columns
to support this relationship.
We may need to query each workspace_type differently - perhaps
they live in separate tables (home_offices, coworking_spaces, etc). So, when fetching the relationship, we’ll need to group our Employees by workspace_type and query differently for each group:
# app/resources/employee_resource.rb
polymorphic_belongs_to :workspace,
group_by: :workspace_type,
groups: {
'Office' => {
scope: -> { Office.all },
resource: OfficeResource,
foreign_key: :workspace_id
},
'HomeOffice' => {
scope: -> { HomeOffice.all },
resource: HomeOfficeResource,
foreign_key: :workspace_id
}
}Let’s say our API was returning 10 Employees, sideloading their corresponding Workspace. The underlying code would:
- Fetch the employees
- Group the employees by the given key:
employees.group_by { |e| e.workspace_type } - Use the
Officeconfiguration for allEmployees whereworkspace_typeisOffice, and use theHomeOfficeconfiguration for allEmployees whereworkspace_typeisHomeOffice, and so forth.