Relationships and Nested Queries
Let’s say we want to fetch a Post
and all of its Comment
s:
Using the default ActiveRecord
Adapter, we would add this code to our
PostResource
:
Note: we’d have to whitelist
comments
in our serializer as well.
To understand this code, we first have to realize that this is a Macro - code that is generating lower-level code for the purposes of removing boilerplate. Let’s understand the lower-level DSL before breaking down the macro.
This is the lower-level allow_sideload
DSL. There are four things
going on. To begin with:
- We’ve whitelisted
comments
. Without this, the request would raise the errorJsonapiCompliable::Errors::InvalidInclude
. This ensures clients can’t arbitrarily pull back data that could introduce performance problems or security risks. - We’ve said, “when retrieving comments, re-use the logic defined in
CommentResource
”. This way all the filter, sorting, etc query logic at the/comments
endpoint can be reused when sideloading comments from the/posts?include=comments
endpoint.
That brings us to the scope
and assign
hooks. When querying a
relationship, we need to answer two questions:
- Given a list of parents (
post
s), how should we scope the request for children (comment
s)? This is thescope
block. In a relational database, we’d usually scope based on foreign and primary keys. - Given a list of parents (
post
s) and a list of children (comment
s), how do you want to assign these objects together? This is theassign
block. In a relational database, we’d usually compare foreign and primary keys.
In other words, the code would look similar to this for ActiveRecord
:
Note that scope
hasn’t actually fired a query - we take the result of
this block and pass it to CommentResource
so that further query logic
(filtering, sorting, etc) can be applied and re-used across endpoints.
Of course, the code above would be very tedious to write by hand every
time. That’s why we have Macros like has_many
, belongs_to
etc -
configure only the parts you need, and avoid the boilerplate:
Given the above options, we can auto-generate allow_sideload
code. You
can always write allow_sideload
directly if you have highly customized
logic. You can also pass a block to the macros to customize:
Again, nested queries come for free. This code allows for nested queries
like “give me the post, and its active
comments”: