@@ -165,128 +165,6 @@ a list of user ids in one call.
165
165
166
166
That said, with key caching turn on (the default), it will still be more efficient using ` dataloader ` than without it.
167
167
168
- # Using dataloader in graphql for maximum efficiency
169
-
170
-
171
- If you are using ` graphql ` , you are likely to making queries on a graph of data (surprise surprise). ` dataloader ` will help
172
- you to make this a more efficient process by both caching and batching requests for that graph of data items. If ` dataloader `
173
- has previously see a data item before, it will cached the value and will return it without having to ask for it again.
174
-
175
- Imagine we have the StarWars query outlined below. It asks us to find a hero and their friend's names and their friend's friend's
176
- names. It is likely that many of these people will be friends in common.
177
-
178
-
179
-
180
- {
181
- hero {
182
- name
183
- friends {
184
- name
185
- friends {
186
- name
187
- }
188
- }
189
- }
190
- }
191
-
192
- The result of this query is displayed below. You can see that Han, Leia, Luke and R2-D2 are tight knit bunch of friends and
193
- share many friends in common.
194
-
195
-
196
- [hero: [name: 'R2-D2', friends: [
197
- [name: 'Luke Skywalker', friends: [
198
- [name: 'Han Solo'], [name: 'Leia Organa'], [name: 'C-3PO'], [name: 'R2-D2']]],
199
- [name: 'Han Solo', friends: [
200
- [name: 'Luke Skywalker'], [name: 'Leia Organa'], [name: 'R2-D2']]],
201
- [name: 'Leia Organa', friends: [
202
- [name: 'Luke Skywalker'], [name: 'Han Solo'], [name: 'C-3PO'], [name: 'R2-D2']]]]]
203
- ]
204
-
205
- A naive implementation would called a ` DataFetcher ` to retrieved a person object every time it was invoked.
206
-
207
- In this case it would be * 15* calls over the network. Even though the group of people have a lot of common friends.
208
- With ` dataloader ` you can make the ` graphql ` query much more efficient.
209
-
210
- As ` graphql ` descends each level of the query ( eg as it processes ` hero ` and then ` friends ` and then for each their ` friends ` ),
211
- the data loader is called to "promise" to deliver a person object. At each level ` dataloader.dispatch() ` will be
212
- called to fire off the batch requests for that part of the query. With caching turned on (the default) then
213
- any previously returned person will be returned as is for no cost.
214
-
215
- In the above example there are only * 5* unique people mentioned but with caching and batching retrieval in place their will be only
216
- * 3* calls to the batch loader function. * 3* calls over the network or to a database is much better than * 15* calls you will agree.
217
-
218
- If you use capabilities like ` java.util.concurrent.CompletableFuture.supplyAsync() ` then you can make it even more efficient by making the
219
- the remote calls asynchronous to the rest of the query. This will make it even more timely since multiple calls can happen at once
220
- if need be.
221
-
222
- Here is how you might put this in place:
223
-
224
-
225
- ``` java
226
-
227
- // a batch loader function that will be called with N or more keys for batch loading
228
- BatchLoader<String , Object > characterBatchLoader = new BatchLoader<String , Object > () {
229
- @Override
230
- public CompletionStage<List<Object > > load (List<String > keys ) {
231
- //
232
- // we use supplyAsync() of values here for maximum parellisation
233
- //
234
- return CompletableFuture . supplyAsync(() - > getCharacterDataViaBatchHTTPApi(keys));
235
- }
236
- };
237
-
238
- // a data loader for characters that points to the character batch loader
239
- DataLoader characterDataLoader = new DataLoader<String , Object > (characterBatchLoader);
240
-
241
- //
242
- // use this data loader in the data fetchers associated with characters and put them into
243
- // the graphql schema (not shown)
244
- //
245
- DataFetcher heroDataFetcher = new DataFetcher () {
246
- @Override
247
- public Object get (DataFetchingEnvironment environment ) {
248
- return characterDataLoader. load(" 2001" ); // R2D2
249
- }
250
- };
251
-
252
- DataFetcher friendsDataFetcher = new DataFetcher () {
253
- @Override
254
- public Object get (DataFetchingEnvironment environment ) {
255
- StarWarsCharacter starWarsCharacter = environment. getSource();
256
- List<String > friendIds = starWarsCharacter. getFriendIds();
257
- return characterDataLoader. loadMany(friendIds);
258
- }
259
- };
260
-
261
- //
262
- // DataLoaderRegistry is a place to register all data loaders in that needs to be dispatched together
263
- // in this case there is 1 but you can have many
264
- //
265
- DataLoaderRegistry registry = new DataLoaderRegistry ();
266
- registry. register(" character" , characterDataLoader);
267
-
268
- //
269
- // this instrumentation implementation will dispatched all the dataloaders
270
- // as each level fo the graphql query is executed and hence make batched objects
271
- // available to the query and the associated DataFetchers
272
- //
273
- DataLoaderDispatcherInstrumentation dispatcherInstrumentation
274
- = new DataLoaderDispatcherInstrumentation (registry);
275
-
276
- //
277
- // now build your graphql object and execute queries on it.
278
- // the data loader will be invoked via the data fetchers on the
279
- // schema fields
280
- //
281
- GraphQL graphQL = GraphQL . newGraphQL(buildSchema())
282
- .instrumentation(dispatcherInstrumentation)
283
- .build();
284
- ```
285
-
286
- One thing to note is the above only works if you use ` DataLoaderDispatcherInstrumentation ` which makes sure ` dataLoader.dispatch() ` is called. If
287
- this was not in place, then all the promises to data will never be dispatched ot the batch loader function and hence nothing would ever resolve.
288
-
289
- See below for more details on ` dataLoader.dispatch() `
290
168
291
169
### Error object is not a thing in a type safe Java world
292
170
0 commit comments