GraphqlLazyLoad
Lazy executor for activerecord associations and graphql gem.
Installation
GraphqlLazyLoad requires ActiveRecord >= 4.1.16 and Graphql >= 1.3.0. To use add this line to your application's Gemfile:
gem 'graphql_lazy_load', '~> 0.2.0'
Then run bundle install.
Or install it yourself as:
$ gem install graphql_lazy_load
Usage
ActiveRecordRelation
To use, first add the executor (GraphqlLazyLoad::ActiveRecordRelation) to graphqls schema:
class MySchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
lazy_resolve(GraphqlLazyLoad::ActiveRecordRelation, :result)
end
Now you can start using it! Wherever you have an association, the syntax is:
field :field_name, Types::AssociationType, null: false
def field_name
GraphqlLazyLoad::ActiveRecordRelation.new(self, :association_name)
end
Custom
If you want to lazy load a non active record association you can use the Custom loader, first add the executor (GraphqlLazyLoad::Custom) to graphqls schema (both can be in the schema together):
class MySchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
lazy_resolve(GraphqlLazyLoad::Custom, :result)
end
Now you can start using it! Wherever you have a something to lazy load, the syntax is:
field :field_name, Types::AssociationType, null: false
def field_name
GraphqlLazyLoad::Custom.new(self, :association_name, object.id) do |ids|
# return a hash with key matching object.id
AssociationName.where(id: ids).reduce({}) do |acc, value|
acc[value.id] = value
acc
end
end
end
Values passed to the Custom initializer are self, unique_identifier, unique_id (gets passed to block, and is used to retrieve the values), you can also pass an optional value params (gets passed as second argument to block). It is important to note that data is grouped by unique_identifier and params.
Examples
If you have two models Team which can have many Players. To lazy load players from teams do the following:
ActiveRecordRelation
module Types
class TeamType < Types::BaseObject
...
field :players, [Types::PlayerType], null: false
def players
GraphqlLazyLoad::ActiveRecordRelation.new(self, :players)
end
end
end
And to lazy load teams from players do the following:
module Types
class PlayerType < Types::BaseObject
...
field :team, Types::TeamType, null: false
def team
GraphqlLazyLoad::ActiveRecordRelation.new(self, :team)
end
end
end
Custom
module Types
class TeamType < Types::BaseObject
...
field :players, [Types::PlayerType], null: false
def players
GraphqlLazyLoad::Custom.new(self, :players, object.id) do |team_ids|
# return a hash with key matching object.id
Player.where(team_id: team_ids).reduce({}) do |acc, player|
(acc[player.team_id] ||= []).push(player)
acc
end
end
end
end
end
And to lazy load teams from players do the following:
module Types
class PlayerType < Types::BaseObject
...
field :team, Types::TeamType, null: false
def team
GraphqlLazyLoad::Custom.new(self, :team, object.team_id) do |team_ids|
# return a hash with key matching object.team_id
Team.where(id: team_ids).reduce({}) do |acc, team|
acc[team.id] = team
acc
end
end
end
end
end
Scoping
The great thing is you can pass scopes/params! So for the example above if you want to allow sorting (or query, paging, etc) do the following.
ActiveRecordRelation
module Types
class PlayerType < Types::BaseObject
...
field :players, [Types::PlayerType], null: false do
argument :order, String, required: false
end
def players(order: nil)
scope = Player.all
scope = scope.sort(order.underscore) if order
GraphqlLazyLoad::ActiveRecordRelation.new(self, :players, scope: scope)
end
end
end
Custom
module Types
class PlayerType < Types::BaseObject
...
field :players, [Types::PlayerType], null: false do
argument :order, String, required: false
end
def players(order: nil)
GraphqlLazyLoad::Custom.new(self, :players, object.id, {order: order}) do |team_ids, params|
query = Player.where(team_id: team_ids)
query = query.order(params[:order].underscore) if params[:order]
query.reduce({}) do |acc, player|
(acc[player.team_id] ||= []).push(player)
acc
end
end
end
end
end
To test this out try the example app at graph_test
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/jonathongardner/graphql_lazy_load. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the GraphqlLazyLoad project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.