schema {
query: Query
}
scalar DateTime
type Query {
countries: [Country!]!
}
type Country {
users(activeSince: DateTime!): [User!]!
}
type User {
id: ID!
}
The key here is that Country.users takes an argument that filters the list of users to be returned. Currently there is no good way to handle this. EagerLoadChildrenOfType doesn't get access to the field arguments so your only option is to load all the users and apply the filtering in the resolver function. That means you will potentially load way more users than necessary which is not great.
This PR is the first step towards fixing this issue.
Getting field arguments from QueryTrail
The first step to fixing this is making juniper_from_schema::QueryTrail aware of field arguments. That is happening here. At a high level getting the arguments from QueryTrail works like so
fn field_countries(
&self,
exec: &Executor<'_, Context>,
trail: QueryTrail<'_, Country, Walked>,
) -> FieldResult<Vec<Country>> {
// get the arguments to the `users` field. They'll be a `CountryUsersArgs` struct
let args: CountryUsersArgs = trail.users_args();
// get the value of `activeSince`
let active_since: chrono::DateTime = args.active_since();
// `CountryUsersArgs` has no other methods since `Country.users` only takes one argument
}
So all fields that take arguments will result in a method on QueryTrail called {name of field}_args that will return a struct named {name of type}{name of field}Args which have methods for each concrete argument the field takes.
Eager loading fields with arguments
These are the high level changes made to support eager loading fields with arguments:
Add new associated type to EagerLoadChildrenOfType called FieldArguments. This is for the type of the arguments struct returned by QueryTrail, e.g. CountryUsersArgs from above.
Pass these arguments to user defined methods on EagerLoadChildrenOfType:
child_ids
load_children
is_child_of
This is to allow users to customize things based on the arguments at each level.
The code generation always sets EagerLoadChildrenOfType::FieldArguments = (). That means you'll get a compile error if you're trying to eager load a field that takes arguments. If the field doesn't take arguments everything will continue working as previous.
That also means users are required to implement EagerLoadChildrenOfType manually for all fields that take arguments. This is kinda tedious but I think time will tell if generating implementations that rely solely on LoadFrom is possible.
LoadFrom gets a second generic argument which defaults to (). This type is for the type field arguments (e.g. CountryUsersArgs). That fact that it defaults to () means existing implementations of LoadFrom will continue to work for fields that don't take arguments, but special implementations will be required for fields that do. The compiler will tell you exactly which implementations you need.
TODO
[x] Test this on @tonsser's GraphQL API
[x] Improve error message when eager loading field that takes arguments
Fixes #18
Imagine you had a GraphQL schema like this:
The key here is that
Country.users
takes an argument that filters the list of users to be returned. Currently there is no good way to handle this.EagerLoadChildrenOfType
doesn't get access to the field arguments so your only option is to load all the users and apply the filtering in the resolver function. That means you will potentially load way more users than necessary which is not great.This PR is the first step towards fixing this issue.
Getting field arguments from
QueryTrail
The first step to fixing this is making
juniper_from_schema::QueryTrail
aware of field arguments. That is happening here. At a high level getting the arguments fromQueryTrail
works like soSo all fields that take arguments will result in a method on
QueryTrail
called{name of field}_args
that will return a struct named{name of type}{name of field}Args
which have methods for each concrete argument the field takes.Eager loading fields with arguments
These are the high level changes made to support eager loading fields with arguments:
EagerLoadChildrenOfType
calledFieldArguments
. This is for the type of the arguments struct returned byQueryTrail
, e.g.CountryUsersArgs
from above.EagerLoadChildrenOfType
:child_ids
load_children
is_child_of
EagerLoadChildrenOfType::FieldArguments = ()
. That means you'll get a compile error if you're trying to eager load a field that takes arguments. If the field doesn't take arguments everything will continue working as previous.EagerLoadChildrenOfType
manually for all fields that take arguments. This is kinda tedious but I think time will tell if generating implementations that rely solely onLoadFrom
is possible.LoadFrom
gets a second generic argument which defaults to()
. This type is for the type field arguments (e.g.CountryUsersArgs
). That fact that it defaults to()
means existing implementations ofLoadFrom
will continue to work for fields that don't take arguments, but special implementations will be required for fields that do. The compiler will tell you exactly which implementations you need.TODO