GraphQL Subscription over SSE with NestJS using @graphql-sse/express

Note Statistics

Note Statistics

  • Viewed 722 times
  • Bookmarked 1 times
  • 1 Grateful readers
Sat, 11/06/2021 - 15:59

While Server Sent Events (SSE) are a good candidate for GraphQL Subscriptions, the is still no full support for it in popular packages. For example the GraphQL Playground only works with Subscriptions over Websockets. Also, in the @nestjs/graphql package, the recommended package for using GraphQL with NestJS, the implementation of subscription is hard-coded to work with either graphql-ws or subscription-transport-ws packages. However, there is a workaround to get things working with @graphql-sse/express.

Implementing Subscriptions over SSE

With @graphql-sse/express you have two different endpoints

  • <host>/graphql: used for GraphQL Queries and Mutations. This is the default behavior added by @nestjs/graphql
  • <host>/graphql-subscription: used for GraphQL Subscriptions. This endpoint is added by the @graphql-sse/express package.

Adding Subscription over SSE involves a small update in the main module that imports the GraphQLModule. When importing the GraphQLModule we will not use the installSubscriptionHandlers or subscriptions option, since they are used for subscription over Websockets. Instead we will use the OnModuleInit to create a new SubscriptionServer from the @graphql-sse/express. The SubscriptionServer creates a new subscription endpoint with the path /graphql-subscription . The SubscriptionServer requires the schema and express App which accessible via Injected GraphQLSchemaHost and HttpAdapterHost respectively.

import { Module, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { GraphQLModule, GraphQLSchemaHost } from '@nestjs/graphql';
import { GreetingResolver } from './resolvers/greeting.resolver';
import { SubscriptionServer } from '@graphql-sse/express';
import { HttpAdapterHost } from '@nestjs/core';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true
    }),
  ],
  controllers: [],
  providers: [
    GreetingResolver,
  ]
})
export class GQLModule implements OnModuleInit , OnModuleDestroy {
  private subscriptionServer: SubscriptionServer;

  constructor(
    private readonly httpAdapterHost: HttpAdapterHost,
    private graphQLSchemaHost: GraphQLSchemaHost
  ) {}
	
  onModuleInit() {
    const schema = this.graphQLSchemaHost.schema;
    const app = this.httpAdapterHost.httpAdapter.getInstance();
   this.subscriptionServer  = SubscriptionServer.create({
      schema,
      app,
    });
  }
	
onModuleDestroy() {
    this.subscriptionServer.close()
  }
}

What is missing?

This solution is still not perfect. It is not as clean as using GraphQLModule.forRoot() with subscription option. Also we cannot use subscriptions in the GraphQL Playground. Hopefully soon, both the @nestjs/graphql and GraphQL Playground will provide a better abstraction for subscriptions, and allows the usage of any type of transport implementation.

See a full working example here.

Authored by
Tags