Manual Parallelism

You can coordinate with GraphQL::Dataloader to run tasks in the background. To do this, call dataloader.yield inside Source#fetch after kicking off your task. For example:

def fetch(ids) # somehow queue up a background query, # see examples below future_result = async_query_for(ids) # return control to the dataloader dataloader.yield # dataloader will come back here # after calling other sources, # now wait for the value future_result.value end 

Alternatively, you can use AsyncDataloader to automatically background I/O inside Source#fetch calls.

Example: Rails load_async

You can use Rails’s load_async method to load ActiveRecord::Relations in the background. For example:

class Sources::AsyncRelationSource < GraphQL::Dataloader::Source def fetch(relations) relations.each(&:load_async) # start loading them in the background dataloader.yield # hand back to GraphQL::Dataloader relations.each(&:load) # now, wait for the result, returning the now-loaded relation end end 

You could call that source from a GraphQL field method:

field :direct_reports, [Person] def direct_reports # prepare an ActiveRecord::Relation: direct_reports = Person.where(manager: object) # pass it off to the source: dataloader .with(Sources::AsyncRelationSource) .load(direct_reports) end 

Example: Rails async calculations

In a Dataloader source, you can run Rails async calculations in the background while other work continues. For example:

class Sources::DirectReportsCount < GraphQL::Dataloader::Source def fetch(users) # Start the queries in the background: promises = users.map { |u| u.direct_reports.async_count } # Return to GraphQL::Dataloader: dataloader.yield # Now return the results, waiting if necessary: promises.map(&:value) end end 

Which could be used in a GraphQL field:

field :direct_reports_count, Int def direct_reports_count dataloader.with(Sources::DirectReportsCount).load(object) end 

Example: Concurrent::Future

You could use concurrent-ruby to put work in a background thread. For example, using Concurrent::Future:

class Sources::ExternalDataSource < GraphQL::Dataloader::Source def fetch(urls) # Start some I/O-intensive work: futures = urls.map do |url| Concurrent::Future.execute { # Somehow fetch and parse data: get_remote_json(url) } end # Yield back to GraphQL::Dataloader: dataloader.yield # Dataloader has done what it can, # so now return the value, waiting if necessary: futures.map(&:value) end end