Skip to content

Example of DataLoaders. #396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 1, 2019
Merged

Example of DataLoaders. #396

merged 1 commit into from
Oct 1, 2019

Conversation

neetkee
Copy link
Contributor

@neetkee neetkee commented Sep 28, 2019

📝 Description

I've asked about using dataLoaders in the related issue, so here is my example of its possible implementation, using an extension of a query handler. What do you think?

Note that I also made ExecutionResult.toGraphQLResponse() function public in this PR.

🔗 Related Issues

#381

@codecov-io
Copy link

codecov-io commented Sep 28, 2019

Codecov Report

Merging #396 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #396   +/-   ##
=========================================
  Coverage     97.62%   97.62%           
  Complexity      324      324           
=========================================
  Files           104      104           
  Lines          1223     1223           
  Branches        198      198           
=========================================
  Hits           1194     1194           
  Misses            9        9           
  Partials         20       20
Impacted Files Coverage Δ Complexity Δ
...pediagroup/graphql/spring/model/GraphQLResponse.kt 100% <ø> (ø) 4 <0> (ø) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4e38e11...4592bc6. Read the comment docs.

@dariuszkuc
Copy link
Collaborator

Thanks for providing the example! I've added default support to SimpleQueryHandler in #400.

Would you mind updating the example to use latest code?

@neetkee
Copy link
Contributor Author

neetkee commented Oct 1, 2019

I guess we need to create DataLoaderRegistry per request, to avoid passing cached values between requests? I'm not sure how to do it with #400. Maybe we need to autowire some factory, that creates dataLoaderRegistry, I saw a similar way in SPQR lib: DataLoaderRegistryFactory

@dariuszkuc
Copy link
Collaborator

Interesting! So data loader registry should be created per request? I've added support for this in #410 by creating factory that allows developers to create new data loader registry instance per query execution. Unsure how useful it is as within graphql-kotlin we generate schemas directly from the code based on the underlying functions and classes (where devs can implement whatever) but I think it is good to have an option to use DataLoader.

@smyrick
Copy link
Contributor

smyrick commented Oct 1, 2019

👍 Plus one to this:

Unsure how useful it is as within graphql-kotlin we generate schemas directly from the code based on the underlying functions and classes

In graphql-java land, I can see how this is a helpful caching mechanism since you don't have a class that is run when you execute a resolver/data fetcher. But with our library you will be executing a function that can be in any scope and fetching data from a downstream service can be cached in any way you want. This feature will be something we can support through the factory but we will defer handling of everything else to the server devs.

@neetkee neetkee closed this Oct 1, 2019
@neetkee neetkee reopened this Oct 1, 2019
@neetkee
Copy link
Contributor Author

neetkee commented Oct 1, 2019

@smyrick

But with our library you will be executing a function that can be in any scope and fetching data from a downstream service can be cached in any way you want.

Could you give me some direction on how it can be implemented?

@smyrick
Copy link
Contributor

smyrick commented Oct 1, 2019

@neetkee

If you have this class and function as your schema object

class Query {
  fun getUserById(id: Int): User? {
    val result = getUserFromSource(id)
    return result
  }
}

the getUserFromSource could be any function that loads data from an API, database, or a cache system. This means that you could handle the caching layer yourself based on more inputs or configs and not solely based on the DataLoader implementation.

@smyrick smyrick added the type: documentation Documentation or test changes label Oct 1, 2019
@smyrick smyrick merged commit 593dc3a into ExpediaGroup:master Oct 1, 2019
@neetkee
Copy link
Contributor Author

neetkee commented Oct 1, 2019

If I understand correctly, we can use it to load from cache/service/etc by id, one by one, but if we want to batch our request, we need to gather all needed ids, and then execute our query.

@dariuszkuc
Copy link
Collaborator

If I understand correctly, we can use it to load from cache/service/etc by id, one by one, but if we want to batch our request, we need to gather all needed ids, and then execute our query.

Yep. It all drills down to how you gather those IDs -> you need to know all of them to batch regardless whether you use data loader or not.


@Component("CompanyDataFetcher")
@Scope("prototype")
class CompanyDataFetcher : DataFetcher<CompletableFuture<Company>>, BeanFactoryAware {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smyrick hey. Am I understand that correctly that this thing will be called anytime somebody queries the company field company
lateinit var Company on Employee and ONLY the Employee, and not other classes?
Or I have to branch here or whatever?

Maybe some drawing or ASCII or draw.io graph will help me understand the connection between beans and the whole resolve cycle as I'm unsure :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RIP21 The DataFetcher is the name of the "resolver" for graphql-java. You can see the docs here: https://www.graphql-java.com/documentation/v16/data-fetching/

This is a data fetcher for the entire Company class. GraphQL Kotlin normally would handle creating the different data fetchers for you, but in this case the user wanted have a DataLoader which is a separate GraphQL specific caching mechanism. The DataFetcher is invoked when ever there is a field that returns a Company and, using DataLoaders, they are cache the company info for each employee so they don't have to make multiple calls to get the the duplicate company data for all the employees

dariuszkuc pushed a commit to dariuszkuc/graphql-kotlin that referenced this pull request Aug 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation Documentation or test changes
Development

Successfully merging this pull request may close these issues.

5 participants