Dataloaders

It is possible to create context, and consequently dataloaders, in both a request scope and a per query scope by customizing GraphQLContextBuilder and selecting the appropriate ContextSetting with the provided GraphQLConfiguration. A new DataLoaderRegistry should be created in each call to the GraphQLContextBuilder, and the servlet will call the builder at the appropriate times. For example:

public class CustomGraphQLContextBuilder implements GraphQLServletContextBuilder {

  private final DataLoader userDataLoader;
    
  public CustomGraphQLContextBuilder(DataLoader userDataLoader) {
    this.userDataLoader = userDataLoader;
  }
    
  public GraphQLContext build() {
    return new DefaultGraphQLContext();
  }
    
  public GraphQLContext build(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
    return DefaultGraphQLServletContext.createServletContext()
            .with(httpServletRequest)
            .with(httpServletResponse)
            .with(buildDataLoaderRegistry())
            .build();
  }
    
  public GraphQLContext build(Session session, HandshakeRequest handshakeRequest) {
    return DefaultGraphQLWebSocketContext.createWebSocketContext()
            .with(session)
            .with(handshakeRequest)
            .with(buildDataLoaderRegistry())
            .build();
  }
    
  private DataLoaderRegistry buildDataLoaderRegistry() {
    DataLoaderRegistry registry = new DataLoaderRegistry();
    registry.register("userDataLoader", userDataLoader);
    return registry;
  }
}

It is then possible to access the DataLoader in the resolvers by accessing the DataLoaderRegistry from context. For example:

public CompletableFuture<String> getEmailAddress(User user, DataFetchingEnvironment dfe) { // User is the graphQL type
 final DataLoader<String, UserDetail> userDataloader =
   dfe.getContext().getDataLoaderRegistry().get().getDataLoader("userDataLoader"); // UserDetail is the data that is loaded

   return userDataloader.load(User.getName())
     .thenApply(userDetail -> userDetail != null ? userDetail.getEmailAddress() : null);
}

If per request is selected this will cause all queries within the http request, if using a batch, to share dataloader caches and batch together load calls as efficiently as possible. The dataloaders are dispatched using instrumentation and the correct instrumentation will be selected according to the ContextSetting. The default context setting in GraphQLConfiguration is per query.

Two additional context settings are provided, one for each of the previous settings but without the addition of the Dataloader dispatching instrumentation. This is useful for those not using Dataloaders or wanting to supply their own dispatching instrumentation though the instrumentation supplier within the GraphQLQueryInvoker.