Relations Decorator
Relations
Resolver param decorator which returns an array of relation paths which can be passed through
to the TypeORM data layer in order to join only the required relations. This works by inspecting
the GraphQL info
object, examining the field selection, and then comparing this with information
about the return type's relations.
In addition to analyzing the field selection, this decorator also checks for any @Calculated()
properties on the entity, and additionally includes relations from the relations
array of the calculated
metadata, if defined.
So if, for example, the query only selects the id
field of an Order, then no other relations need
be joined in the resulting SQL query. This can massively speed up execution time for queries which do
not include many deep nested relations.
Example
@Query()
@Allow(Permission.ReadOrder)
orders(
@Ctx() ctx: RequestContext,
@Args() args: QueryOrdersArgs,
@Relations(Order) relations: RelationPaths<Order>,
): Promise<PaginatedList<Order>> {
return this.orderService.findAll(ctx, args.options || undefined, relations);
}
In the above example, given the following query:
Example
{
orders(options: { take: 10 }) {
items {
id
customer {
id
firstName
lastName
}
totalQuantity
totalWithTax
}
}
}
then the value of relations
will be
['customer', 'lines'']
The 'customer'
comes from the fact that the query is nesting the "customer" object, and the 'lines'
is taken
from the Order
entity's totalQuantity
property, which uses Calculated decorator and defines those relations as dependencies
for deriving the calculated value.
Depth
By default, when inspecting the GraphQL query, the Relations decorator will look 3 levels deep in any nested fields. So, e.g. if
the above orders
query were changed to:
Example
{
orders(options: { take: 10 }) {
items {
id
lines {
productVariant {
product {
featuredAsset {
preview
}
}
}
}
}
}
}
then the relations
array would include 'lines'
, 'lines.productVariant'
, & 'lines.productVariant.product'
- 3 levels deep - but it would
not include 'lines.productVariant.product.featuredAsset'
since that exceeds the default depth. To specify a custom depth, you would
use the decorator like this:
Example
@Relations({ entity: Order, depth: 2 }) relations: RelationPaths<Order>,
Omit
The omit
option is used to explicitly omit certain relations from the calculated relations array. This is useful in certain
cases where we know for sure that we need to run the field resolver anyway. A good example is the Collection.productVariants
relation.
When a GraphQL query comes in for a Collection and also requests its productVariants
field, there is no point using a lookahead to eagerly
join that relation, because we will throw that data away anyway when the productVariants
field resolver executes, since it returns a
PaginatedList query rather than a simple array.
Example
@Relations({ entity: Collection, omit: ['productVariant'] }) relations: RelationPaths<Collection>,