gql-live-query
gql-live-query adds Redis-backed GraphQL live-query support to NestJS applications. It uses a local in-memory live-query store in each application instance and Redis to broadcast invalidations between instances.
When application data changes, invalidate the GraphQL resource that depends on it. Connected clients that execute a query with the @live directive receive the query's updated result over graphql-ws.
Requirements
- A running Redis instance.
- A NestJS GraphQL application using the Apollo driver.
- The
graphql-ws,@n1ru4l/graphql-live-query, andgraphqlpackages available to the application.
Usage
- Install the library and its peer dependencies.
- Register
GqlLiveQueryModulewith the Redis connection URL. - Configure the NestJS GraphQL module to use the live-query directive and the injected store's
executewrapper.
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { GraphQLLiveDirective, NoLiveMixedWithDeferStreamRule } from '@n1ru4l/graphql-live-query';
import { GqlLiveQueryModule, RedisLiveQueryStore } from '@org/gql-live-query';
import { execute, subscribe } from 'graphql';
@Module({
imports: [
GqlLiveQueryModule.forRoot({
redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
}),
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
inject: [RedisLiveQueryStore],
useFactory: (liveQueryStore: RedisLiveQueryStore) => ({
path: '/graphql',
autoSchemaFile: true,
subscriptions: {
'graphql-ws': {
execute: liveQueryStore.makeExecute(execute),
subscribe,
} as any,
},
buildSchemaOptions: {
directives: [GraphQLLiveDirective],
},
validationRules: [NoLiveMixedWithDeferStreamRule],
}),
}),
],
})
export class AppModule {}GqlLiveQueryModule is global and exports RedisLiveQueryStore, so it can be injected into services that change data.
Invalidate Live Queries
Call invalidate() after changing data that a live query reads. Use the resource identifier associated with that query. For a query resolver named exampleQuery, the identifier is Query.exampleQuery.
import { Injectable } from '@nestjs/common';
import { RedisLiveQueryStore } from '@org/gql-live-query';
@Injectable()
export class ExampleDataService {
constructor(private readonly liveQueryStore: RedisLiveQueryStore) {}
async updateData(value: string) {
// Persist the updated value here.
await this.liveQueryStore.invalidate('Query.exampleQuery');
}
}invalidate() accepts either one identifier or an array of identifiers. Each invalidation is published through Redis, causing every application instance to refresh its matching local live queries.
Execute a Live Query
Connect a graphql-ws client to the GraphQL WebSocket endpoint and include the @live directive in the query.
import { createClient } from 'graphql-ws';
const client = createClient({
url: 'ws://localhost:3000/graphql',
});
client.subscribe(
{
query: `
query ExampleLiveQuery @live {
exampleQuery
}
`,
},
{
next: (result) => console.log(result),
error: (error) => console.error(error),
complete: () => console.log('Live query completed'),
},
);The client receives an initial result, followed by a new result whenever Query.exampleQuery is invalidated.