# Eager Loading
# Introduction
The JSON:API include
query parameter allows a client to customize
which related resources should be returned in a response document.
If the include
parameter is provided, the response document will be
a Compound Document (opens new window)
containing the subject of the request in the data
member, and the
related resources in the top-level included
member.
In Laravel terms, the include
query parameter allows the client to control
eager loading. (opens new window)
Each JSON:API include path maps to an Eloquent eager load path.
Eager loading via the include
parameter allows a client to reduce the
number of HTTP requests. Ideally, a client should be able to retrieve
the resources they need in a single request for a Compound
Document.
# Eager Loading Depth
Our implementation allows you to specify the depth to which a client
can request eager loading. This is defined on the resource type's
schema and by default is set to a depth of 1
.
To illustrate this, take the following PostSchema
:
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
/**
* @inheritDoc
*/
public function fields(): array
{
return [
ID::make(),
// ...attribute fields
BelongsTo::make('user'),
HasMany::make('tags'),
HasMany::make('comments'),
];
}
With a default depth of 1
, the client will be allowed to request
the following include paths:
user
tags
comments
If we increase the max depth to 2
, the client will be allowed to
request paths through the relationships. For example:
user
user.profile
tags
comments
comments.user
# Setting the Depth
To set the depth of include paths on a resource type, use the
$maxDepth
property:
class PostSchema extends Schema
{
/**
* The maximum depth of include paths.
*
* @var int
*/
protected int $maxDepth = 3;
}
WARNING
You probably do not want to use a large maximum depth - typically,
a maximum value of 3
or 4
is ideal. For example, the
Stripe API (opens new window)
allows a maximum depth of 4
levels.
# Disabling On Specific Relations
Typically you should prevent eager loading on relationships that could (even if only in theory) return hundreds of resources. This is because it would be exceptionally inefficient for a client to get a compound document that contains hundreds or thousands of related resources.
Take for example a posts
resource with a comments
relationship.
If our blog application is extremely popular, a post could have hundreds
or thousands of comments. In such a scenario, we would want to prevent
the comments
relationship from being eager loaded, by using the
cannotEagerLoad
method:
HasMany::make('comments')->cannotEagerLoad()
TIP
In this scenario, our include path depth implementation will know not to allow any paths through the relation that is marked as not allowed for eager loading.
In this scenario, we would instead expect an API client to receive
paginated comments
for a posts
resource, by making a request to
the comments
relationship endpoint:
GET /api/v1/posts/123/comments?page[size]=20&page[number]=1&include=user HTTP/1.1
Accept: application/vnd.api+json
In this request, the client asks for the first 20 comments for the posts
resource with an id
of 123
. It also asks for the user
of the comments
to be included, so that it can display each comment with the user who
created it.
TIP
As this relationship endpoint returns comments
resources, the allowed
include paths are determined by the CommentSchema
class.
# Conditionally Disabling on Specific Relations
If you need to conditionally disable eager-loading on a relationship, use
the canEagerLoad()
method. You can provide a boolean, for example:
HasMany::make('comments')->canEagerLoad(
Gate::allows('viewComments', Post::class),
)
Alternatively, you can provide a callback to determine if eager loading is allowed:
HasMany::make('comments')->canEagerLoad(
fn() => Gate::allows('viewComments', Post::class),
)
# Disabling On A Resource Type
To disable eager loading for a resource type, set the $maxDepth
property
to zero:
class PostSchema extends Schema
{
/**
* The maximum depth of include paths.
*
* @var int
*/
protected int $maxDepth = 0;
}
# Specifying Custom Paths
If you do not want to use our depth implementation, you can specify
allowed include paths on a resource type by implementing the includePaths
method on your schema. For example:
public function includePaths(): iterable
{
return [
'comments',
'comments.user',
'tags',
];
}
# Eager Loading Attribute Values
If any of your resource's attributes are derived from related models, you
should use the attribute's on()
method to set the relationship.
This is described in the attributes chapter,
but an example looks like this:
Str::make('description')->on('profile')
When you do this, we will automatically eager load the relationships from which the attributes are derived.
# Additional Eager Loading
If for any reason there are additional relationships that you always need to
eager load, you can add the relationship to the $with
property of your schema.
This property instructs the schema to always eager load the listed model
relationships when retrieving the resource.
For example:
/**
* The relationships that should be eager loaded.
*
* @var array
*/
protected array $with = ['profile'];
TIP
Generally you will not need to add default eager load paths. If a model
relationship is available on your resource as a JSON:API relationship, then
eager loading is controlled via the include
query parameter. If eager loading
is required for an attribute, you should use the attribute's on()
method to
specify the relationship that needs to be eager loaded.
# Default Include Paths
If you want to set include paths that should be used when the client provides
none, these must be set on the query parameters request class for the resource,
e.g. PostQuery
and PostCollectionQuery
. Use the $defaultIncludePaths
property, as described in the query parameters chapter.
← Relationships Sorting →