0.01
The project is in a healthy, maintained state
Common loaders for various database or cache operations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Project Readme

GraphQL::Sources

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add graphql-sources

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install graphql-sources

The GraphQL::Dataloader plugin must be installed in the schema:

class AppSchema < GraphQL::Schema
  use GraphQL::Dataloader
  # ...
end

Usage

Loading belongs_to / has_many Associations

class Purchase < ActiveRecord::Base
  belongs_to :customer
end
class Customer < ActiveRecord::Base
  has_many :purchases
end
class PurchaseType < GraphQL::Schema::Object
  field :customer, CustomerType, null: false

  # @return [Customer]
  def customer
    # SELECT * FROM "customers" WHERE "customers"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :customer)
      .load(object)
  end
end
class CustomerType < GraphQL::Schema::Object
  field :purchases, [PurchaseType], null: false

  def purchases
    # SELECT * FROM "customers" WHERE "customers"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :purchases)
      .load(object)
  end
end

Loading belongs_to / has_one Associations

class Profile < ActiveRecord::Base
  belongs_to :user
end
class User < ActiveRecord::Base
  has_one :profile
end
class ProfileType < GraphQL::Schema::Object
  field :user, [UserType], null: false

  # @return [User]
  def user
    # SELECT * FROM "users" WHERE "users"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :user)
      .load(object)
  end
end
class UserType < GraphQL::Schema::Object
  field :profile, [ProfileType], null: false

  # @return [Profile]
  def profile
      # SELECT * FROM "profiles" WHERE "profiles"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :profile)
      .load(object)
  end
end

Loading has_and_belongs_to_many Associations

class Student
  has_and_belongs_to_many :courses
end
class Course
  has_and_belongs_to_many :students
end
class StudentType < GraphQL::Schema::Object
  field :courses, [CourseType], null: false

  # @return [Array<Course>]
  def courses
    # SELECT * FROM "courses_students" WHERE "courses_students"."student_id" = IN (...)
    # SELECT * FROM "courses" WHERE "courses"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :courses)
      .load(object)
  end
end
class CourseType < GraphQL::Schema::Object
  field :students, [StudentType], null: false

  # @return [Array<Student>]
  def students
    # SELECT * FROM "courses_students" WHERE "courses_students"."course_id" = IN (...)
    # SELECT * FROM "students" WHERE "students"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordAssociation, :students)
      .load(object)
  end
end

Loading has_one_attached Associations

class User
  has_one_attached :photo
end
class UserType < GraphQL::Schema::Object
  field :avatar, AttachedType, null: false

  # @return [ActiveStorage::Attachment]
  def avatar
    # SELECT "active_storage_attachments".*
    # FROM "active_storage_attachments"
    # WHERE "active_storage_attachments"."name" = 'avatar'
    #   AND "active_storage_attachments"."record_type" = 'User'
    #   AND "active_storage_attachments"."record_id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveStorageHasOneAttached, :avatar)
      .load(object)
  end
end

Loading has_many_attached Associations

class User
  has_many_attached :photos
end
class UserType < GraphQL::Schema::Object
  field :photos, [AttachedType], null: false

  # @return [Array<ActiveStorage::Attachment>]
  def photos
    # SELECT "active_storage_attachments".*
    # FROM "active_storage_attachments"
    # WHERE "active_storage_attachments"."name" = 'photos'
    #   AND "active_storage_attachments"."record_type" = 'User'
    #   AND "active_storage_attachments"."record_id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveStorageHasManyAttached, :photos)
      .load(object)
  end
end

Loading ActiveRecord Object / ActiveRecord Collection

class Event < ActiveRecord::Base
  belongs_to :device
end
class Device < ActiveRecord::Base
  has_many :events
end
class EventType < GraphQL::Schema::Object
  field :device, DeviceType, null: false

  # @return [Device]
  def device
    # SELECT * FROM "devices" WHERE "devices"."id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordObject, ::Device, key: :id)
      .load(object.device_id)
  end
end
class DeviceType < GraphQL::Schema::Object
  field :events, [EventType], null: false

  def events
    # SELECT * FROM "events" WHERE "events"."device_id" IN (...)
    dataloader
      .with(GraphQL::Sources::ActiveRecordCollection, ::Event, key: :device_id)
      .load(object)
  end
end

Loading Counts

class Like
  belongs_to :post
end
class Post
  has_many :likes
end
class PostType < GraphQL::Schema::Object
  field :likes, Integer, null: false

  def likes
    dataloader
      .with(GraphQL::Sources::ActiveRecordCount, ::Like, key: :post_id)
      .load(object.id)
  end
end
SELECT "likes"."post_id", COUNT(*)
FROM "likes"
WHERE "likes"."post_id" IN (1, 2, 3, ...)
GROUP BY "likes"."post_id"

Loading Exists

class User
  has_many :purchases
end
class Purchase
  belongs_to :product
  belongs_to :user
end
class Product
  has_many :purchases
end
class ProductType
  field :purchased, Boolean, null: false

  def purchased
    dataloader
      .with(GraphQL::Sources::ActiveRecordExists, ::Purchase.where(user: context.user), key: :product_id)
      .load(object.id)
  end
end

Loading with Rails.cache

class UserType < GraphQL::Schema::Object
  field :location, String, null: false

  def location
    dataloader
      .with(GraphQL::Sources::RailsCache)
      .load(key: "geocode:#{object.latest_ip}", fallback: -> { Geocode.for(object.latest_ip) })
  end
end

Status

CircleCI Maintainability Test Coverage

License

The gem is available as open source under the terms of the MIT License.