Kasheftin
Kasheftin

Reputation: 9443

Nest.JS graphql subscription does not work with DataLoader (context is not defined)

I have a nestjs graphql application which uses DataLoader. It works fine for queries and mutations, but it does not work for subscriptions. This is how configuration is defined in app.module:

@Module({
  imports: [
    ...
    GraphQLModule.forRootAsync({
      driver: ApolloDriver,
      imports: [TasksModule],
      inject: [TasksService],
      useFactory: (tasksService: TasksService) => ({
        playground: true,
        autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
        context: () => createTaskLoaders(tasksService),
        subscriptions: {
          'graphql-ws': true,
          'subscriptions-transport-ws': true
        }
      })
    })
  ]
})
export class AppModule {}

This is how context is used in the resolver:

  @ResolveField()
  async fieldSet(@Parent() task: Task, @Context() context) {
    console.log('context', Object.keys(context)) // The output is empty when it runs for subscription
    return context.someCustomLoader.load(task.id)
  }

It does not matter what createTaskLoaders and someCustomLoader are - they are just custom functions. The issue is that they are not passed to the context when it's used from inside a subscription. I guess some additional configuration is needed for subscriptions case, but I can not find any working nestjs dataloader + subscriptions example as well as documentation about context for subscriptions.

Upvotes: 1

Views: 1139

Answers (2)

Shinigami
Shinigami

Reputation: 685

I took a slightly alternative solution:

Inside a Resolver you can use the resolve callback and access the context there

@Subscription((returns) => MyEvent, {
  resolve(payload, _args, context, info) {
    const myEvent = payload[info.fieldName];
    context['myDataLoader'].clear(myEvent.referenceKey);
    return myEvent;
  },
})
entityUpdated(): AsyncIterator<unknown, any, undefined> {
  return this.pubSub.asyncIterator('entityUpdated');
}

MyEvent is my own class, so this could something different for you

More described here: https://docs.nestjs.com/graphql/subscriptions#mutating-subscription-payloads

Upvotes: 2

SakisTsalk
SakisTsalk

Reputation: 865

You have to pass the loaders manually in subscriptions using the onConnect

  subscriptions: {
    'graphql-ws': {
      path: '/graphql',
      onConnect: (context: Context) => {
        const { connectionParams, extra } = context;

        extra.loaders = createTaskLoaders(tasksService);
      },
    },
    'subscriptions-transport-ws': {
      path: '/graphql',
      onConnect: (connectionParams) => {
        return {
          loaders: createTaskLoaders(tasksService),
        };
      },
    },
  },

Something like this should work

Upvotes: 2

Related Questions