Published on
2 min read

Solving N+1 query in GraphQL using graphql-batch

Authors

One of the most important pain points in GraphQL is the N+1 SQL queries problem. GraphQL query fields are designed to be stand-alone functions, and resolving those fields individually might result in multiple database requests.

For example, fetching an article with 7 comments might fire 1 query for the article, 1 for the comments, and then 7 separate queries for each comment's user.

N+1 Problem

Shopify created graphql-batch to solve this in Ruby. It provides an executor for the graphql gem that allows queries to be batched.

Installation

gem 'graphql-batch'

1. Define a Custom Loader

# app/graphql/record_loader.rb
require 'graphql/batch'

class RecordLoader < GraphQL::Batch::Loader
  def initialize(model)
    @model = model
  end

  def perform(ids)
    @model.where(id: ids).each { |record| fulfill(record.id, record) }
    ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
  end
end

2. Use the Plugin in Your Schema

# app/graphql/graphql_ruby_sample_schema.rb
GraphqlRubySampleSchema = GraphQL::Schema.define do
  query Types::QueryType
  use GraphQL::Batch
end

3. Use the Loader in Resolvers

Update your CommentType to use the loader:

# app/graphql/types/comment_type.rb
field :user, -> { UserType } do
  resolve -> (obj, args, ctx) {
    RecordLoader.for(User).load(obj.user_id)
  }
end

Using RecordLoader.for(User).load(obj.user_id) makes the user fetching lazy, resolving all IDs in a single batched query.

Results

Before
After

More information is available in the gem documentation. Sample code can be found here.

TwitterLinkedInHacker News