GraphQL without N+1 is easy. N1Loader is a must

от автора

N1Loader is designed to provide a simple way for avoiding N+1 issues
of any kind. Gladly, it’s super easy to integrate with your GraphQL
API. Without further delay, let’s look at a simple but yet detailed
example.

# Add N1Loader with ArLazyPreload integration  require "n1_loader/ar_lazy_preload" require 'graphql'  # Setup SQLite tables in memory. That is irrelevant to the example. require_relative 'context/setup_database' # ArLazyPreload requires Rails application. This is needed to avoid that. require_relative 'context/setup_ar_lazy'  class User < ActiveRecord::Base   has_many :payments    n1_optimized :payments_total do |users|     # Fetch payments for a group of users we will preload in a single query     total_per_user = Payment.group(:user_id).where(user: users).sum(:amount).tap { |h| h.default = 0 }      users.each do |user|       total = total_per_user[user.id]       # No promises here, simply add a value for to a user.       fulfill(user, total)     end   end end  class Payment < ActiveRecord::Base   belongs_to :user    validates :amount, presence: true end  # Enable ArLazyPreload globally ArLazyPreload.config.auto_preload = true # Or use +preload_associations_lazily+ when loading objects from database  class UserType < GraphQL::Schema::Object   field :payments_total, Integer end  class QueryType < GraphQL::Schema::Object   field  :users, [UserType]    def users     User.all   end end  class Schema < GraphQL::Schema   query QueryType end  query_string = <<~GQL   {     users {       paymentsTotal     }   } GQL  # No N+1. And never will be! p Schema.execute(query_string)['data']

N1Loader supports arguments that you can pass through GraphQL API. There will no N+1 still.

class User < ActiveRecord::Base   n1_optimized :payments_total do     argument :from     argument :to      def perform(users)       total_per_user =         Payment           .group(:user_id)           .where(created_at: from..to)           .where(user: users)           .sum(:amount)           .tap { |h| h.default = 0 }        users.each do |user|         total = total_per_user[user.id]         fulfill(user, total)       end     end   end end  class UserType < GraphQL::Schema::Object   field :payments_total, Integer do     argument :from, Time     argument :to, Time   end end  query_string = <<~GQL   {     users {       paymentsTotal     }   } GQL  # No N+1. And never will be! p Schema.execute(query_string, variables: {from: 3.days.ago, to: 1.day.ago})['data']

Define loaders once — use it everywhere without N+1.

Check N1Loader for more features and give it a try in your projects.


ссылка на оригинал статьи https://habr.com/ru/articles/663830/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *