Popularity
2.8
Growing
Activity
6.2
-
220
6
5

Description

Are you tired of fixing N+1 issues? Does it feel unnatural to you to fix it case by case in places where you need the data? We have a solution for you!

It has many benefits as well as integrations with ActiveRecord and ArLazyPreload.

Check them out!

Programming language: Ruby
License: MIT License
Latest version: v1.5.1

N1Loader alternatives and similar gems

Based on the "ORM/ODM Extensions" category.
Alternatively, view n1_loader alternatives based on common mentions on social networks and blogs.

Do you think we are missing an alternative of N1Loader or a related project?

Add another 'ORM/ODM Extensions' Gem

README

N1Loader

CircleCI Gem Version

N1Loader is designed to provide a simple way for avoiding N+1 issues of any kind. For example, it can help with resolving N+1 for:

  • database querying (most common case)
  • 3rd party service calls
  • complex calculations
  • and many more

Toptal is hiring! Join as Freelancer or write me if you want to join Core team.

Support: ActiveRecord 5, 6, and 7.

Killer feature for GraphQL API

N1Loader in combination with ArLazyPreload is a killer feature for your GraphQL API. Give it a try now and see incredible results instantly! Check out the [example](examples/graphql.rb) and start benefiting from it in your projects!

gem 'n1_loader', require: 'n1_loader/ar_lazy_preload'

Enhance ActiveRecord

Are you working with well-known Rails application? Try it out and see how well N1Loader fulfills missing gaps when you can't define ActiveRecord associations! Check out the detailed [guide](guides/enhanced-activerecord.md) with examples or its [short version](examples/active_record_integration.rb).

gem 'n1_loader', require: 'n1_loader/active_record'

Are you ready to forget about N+1 once and for all? Install ArLazyPreload and see dreams come true!

gem 'n1_loader', require: 'n1_loader/ar_lazy_preload'

Standalone mode

Are you not working with ActiveRecord? N1Loader is ready to be used as standalone solution! ([full snippet](examples/core.rb))

gem 'n1_loader'

How to use it?

N1Loader provides DSL that allows you to define N+1 ready loaders that can be injected into your objects in a way that you can avoid N+1 issues.

Disclaimer: examples below are working but designed to show N1Loader potentials only. In real live applications, N1Loader can be applied anywhere and in more [elegant way](examples/isolated_loader.rb).

Let's look at simple example below ([full snippet](examples/active_record_integration.rb)):

class User < ActiveRecord::Base
  has_many :payments

  n1_optimized :payments_total do |users|
    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]
      fulfill(user, total)
    end
  end
end

class Payment < ActiveRecord::Base
  belongs_to :user

  validates :amount, presence: true
end

# A user has many payments. 
# Assuming, we want to know for group of users, what is a total of their payments, we can do the following:

# Has N+1 issue
p User.all.map { |user| user.payments.sum(&:amount) }

# Has no N+1 but we load too many data that we don't actually need
p User.all.includes(:payments).map { |user| user.payments.sum(&:amount) }

# Has no N+1 and we load only what we need
p User.all.includes(:payments_total).map { |user| user.payments_total }

Let's assume now, that we want to calculate the total of payments for the given period for a group of users. N1Loader can do that as well! ([full snippet](examples/arguments_support.rb))

class User < ActiveRecord::Base
  has_many :payments

  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 Payment < ActiveRecord::Base
  belongs_to :user

  validates :amount, presence: true
end

# Has N+1
p User.all.map { |user| user.payments.select { |payment| payment.created_at >= from && payment.created_at <= to }.sum(&:amount) }

# Has no N+1 but we load too many data that we don't need
p User.all.includes(:payments).map { |user| user.payments.select { |payment| payment.created_at >= from && payment.created_at <= to }.sum(&:amount) }

# Has no N+1 and calculation is the most efficient
p User.all.includes(:payments_total).map { |user| user.payments_total(from: from, to: to) }

Features and benefits

  • N1Loader doesn't use Promises which means it's easy to debug
  • Doesn't require injection to objects, can be used in [isolation](examples/isolated_loader.rb)
  • Loads data [lazily](examples/lazy_loading.rb)
  • Loaders can be [shared](examples/shared_loader.rb) between multiple classes
  • Loaded data can be [re-fetched](examples/reloading.rb)
  • Loader can be optimized for [single cases](examples/single_case.rb)
  • Loader support [arguments](examples/arguments_support.rb)
  • Has [integration](examples/active_record_integration.rb) with ActiveRecord which makes it brilliant
  • Has [integration](examples/ar_lazy_integration.rb) with ArLazyPreload which makes it excellent

Funding

Open Collective Backers

You're an individual who wants to support the project with a monthly donation. Your logo will be available on the Github page. [Become a backer]

Open Collective Sponsors

You're an organization that wants to support the project with a monthly donation. Your logo will be available on the Github page. [Become a sponsor]

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/djezzzl/n1_loader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).

License

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

Code of Conduct

Everyone interacting in the N1Loader project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).

Changelog

N1Loader's changelog is available [here](CHANGELOG.md).

Copyright

Copyright (c) Evgeniy Demin. See [LICENSE.txt](LICENSE.txt) for further details.


*Note that all licence references and agreements mentioned in the N1Loader README section above are relevant to that project's source code only.