davidpdrsn / juniper-eager-loading

Library for avoiding N+1 query bugs with Juniper
68 stars 8 forks source link

Fields with arguments #24

Closed davidpdrsn closed 5 years ago

davidpdrsn commented 5 years ago

Fixes #18

Imagine you had a GraphQL schema like this:

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:

TODO