8

Following the Apollo Server docs on dataSources, one would assume that the best way to access data in a GraphQL resolver would be through the dataSources option on the server config.

However, the app I'm working on has requirements that result in many different data source classes (truncated into ... in my handler.ts file below, but there are about 10 now and there will be many more in the future). This means that every data source class will be instantiated for each query/mutation the server receives, which I'm thinking could eventually result in a bit of latency if I have enough data source classes.

I'm thinking of not using the dataSources option and instead just instantiating each data source class as necessary by using the resolver context to call the initialize() method on the Apollo RESTDataSource class (sample resolver method pasted below), which seems to provide all the same functionality to the data source class that the dataSources config option does.

I'm wondering:

  • Are there any benefits / disadvantages of using the dataSources option that I'm not aware of?
  • Why would the Apollo docs encourage you to instantiate all data source classes for each request?

Thanks in advance for any insight you may have!

Server config from handler.ts:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: true,
  playground: true,
  dataSources: (): IDataSources => {
    return {
      entity: new Entity(),
      notes: new Notes(), // this could be removed if "new" resolver method is used
      ... // like 8 more data sources go here
    };
  },
  context: ({
    event,
    context
  }: {
    event: APIGatewayEvent;
    context: Context;
  }): IGraphqlServerOptionContext => {
    const user = new User(event);
    return { apiGatewayEvent: event, apiGatewayContext: context, user };
  },
  extensions: [() => new LogFunctionExtension()]
});

Sample (alternative) resolver method from resolvers.ts:

export const resolvers: IResolvers<any, IResolverContext> = {
  Query: {
    getNotes: async (
      _source,
      args: QueryGetNotesArgs,
      resolverContext: IResolverContext
    ) => {
      validateRequestHeaders('common', resolverContext.apiGatewayEvent);
      // old method of accessing notes data source class:
      // const {
      //   dataSources: { notes }
      // } = resolverContext;

      // new method of accessing notes data source class:
      const notes = new Notes(resolverContext);

      return notes.getNoteDetails(args);
    },
  }
};

new Notes data source class constructor:

export default class Notes extends RESTDataSource<IDataSourceContext> {
  constructor(inputContext: IDataSourceContext) {
    this.initialize({
      context: inputContext,
      cache: new InMemoryLRUCache()
    });
  }
}
dukeluke
  • 646
  • 7
  • 22
  • 1
    Here you're initializing a separate data source *per resolver* and creating a new `InMemoryLRUCache` instance every time. When Apollo calls `initialize`, it uses the same instance that's shared by all datasources. Source [here](https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-core/src/requestPipeline.ts). Under your current approach, you won't be able to take advantage of caching. – Daniel Rearden Jul 01 '19 at 16:00
  • @DanielRearden thanks for the insight. Couldn't I just create and export a cache in the _handler.ts_ file, and then either provide that cache to the server config or just reference that cache in each data source class constructor? – dukeluke Jul 01 '19 at 18:01
  • @DanielRearden or to put that another way, does the `dataSources` option provide any benefits _aside_ from automatically passing the context and cache? – dukeluke Jul 01 '19 at 18:03
  • 1
    You probably want a per-request cache, otherwise you'll end up returning stale data. I'm not familiar enough with the source code to provide an educated answer, which is why I commented instead of posting one :) I suspect if the docs explicitly tell you to do it that way, there may be a good reason for it. Also note, even with a 100 DataSources, you're only initializing some objects, not actually making any requests so I don't think you'll see a noticeable difference in performance. – Daniel Rearden Jul 01 '19 at 18:24

0 Answers0