Step 2: Has Many
We’ll be adding the database table
Because this table tracks all historical positions, we have the
historical_index column. This tells the order the employee moved
through each position, where
1 is most recent.
The Rails Stuff 🚂
Employee model with the association, too:
And update our seed data:
The Graphiti Stuff 🎨
Let’s start by running the same command as before to create
We’ll need to add the association, just like ActiveRecord:
…and a corresponding filter:
If you visit
/api/v1/employees, you’ll see a number of HTTP
that allow lazy-loading positions. Or, if you visit
/api/v1/employees?include=positions, you’ll load the employees and
positions in a single request. We’ll dig a bit deeper into this logic
in the section below.
Before we get there, let’s revisit the
historical_index column. For now, let’s
treat this as an implementation detail that the API should not expose -
let’s say we want to support sorting on this attribute but nothing else:
We’re almost done, but if you run your tests you’ll see two outstanding
errors. This is because Rails 5 belongs_to associations are required by
default. We can’t save a
Position without its corresponding
We can solve this in three ways:
- Turn this off globally, with config.active_record.belongs_to_required_by_default. You may want to do this in test-mode only.
- Turn this off for the specific association:
belongs_to :employee, optional: true.
- Associate an
Employeeas part of the API request.
We’ll take for the last option. Look at
When running our tests, let’s make sure the
reflects the order we created the positions. This code recalculates
everything after a record is saved:
Let’s associate an
Employee. Start by seeding the data:
And associate via
To ensure the
PositionResource will process this relationship, the
last step is to add it:
This will associate the
Position to the
Employee as part of the
creation process. The test should now pass - make the same change to
spec/api/v1/positions/create_spec.rb to get a fully-passing test
Digging Deeper 🧐
Why did we need the
employee_id filter above? To explain that, let’s dive deeper into the logic connecting Resources.
If you hit
/api/v1/employees, you’ll see a number of
Links in the
response. These are useful for lazy-loading, but the same logic
applies to eager loading. Let’s take a look at a Link to see how these
Resources connect together:
The salient bit:
/positions?filter[employee_id]=1. In other words,
fetch all Positions for the given Employee id.That means, whether we’re lazy-loading data in separate requests or
eager-loading in a single request, the same logic fires
This means we need
filter :employee_id, :integer to satisfy the query.
We can customize the logic connecting Resources in a few different ways. First some simple options:
So far so good. The logic, and corresponding Link, both update as you’d
expect (though we’d of course need a corresponding
:emp_id, :integer on
Those options are just simple versions of parameter customization.
You can customize parameters connecting Resources with the
Customizing these params affects the Link as well as the eager-load
logic. Remember the parameters here should reflect the JSON:API
specification, or anything
These are the most common options, but there’s a bunch more. Check out the Resource Relationships Guide to dig even deeper.