subscription in apollo federation using nestJS

How to enable subscription in Apollo Federation with nestJS graphql

NestJS with GraphQL

Setting up a graphQL server in nestJS saves 5x-10x of the effort with vanilla nodejs. NestJS uses the standard apollo library for setting up the graphQL. It supports both normal and federation mode. In federation mode, we can setup graphql servers in every microservices, which can be aggregated using a gateway; Just like API-Gateway with REST services. It is a very good technique if you are running microservices and want to separate the responsibility for each service.

NestJS is one of the powerful nodejs framework available as of today. One of the cool features of nestJS is that it allows easy integration with databases, message queues, authentication etc. Further more it allows developers to create application using nodeJS with in a much stricter, robust and flexible framework like Spring Boot / Angular.

If you are new to nestJS head over to https://docs.nestjs.com for getting started. This article expects that you have a prior knowledge of setting up and running nestJS.

Contents

the problem

All this comes with some inherent caveats from the apollo library. While setting up the federation you cannot use graphql subscription.

You will probably see this error

Apollo had come up with an approach to over come this problem. But it over complicates the situation. Lets see how to tackle this problem using the features in nestJS. In the example i am using the schema first approach. The same approach can be implemented in the code based approach.

solution

The solution to this problem is to hide the subscription based schema for the federation server and host the subscription based graphql as a separate normal graphql server. Seems very simple huh ? Not that easy 🙂 . We will cover the method with some simple steps.

step 1 : Classify Schema

The primary step is to separate your graphql schema in to different files as per the below conventions.

  1. *.graphql : for graphql syntax supported in both federation and normal mode
  2. *.graphql.federation : for syntax supported only in federation mode (eg : extends)
  3. *.graphql.normal : for syntax supported only in normal mode (eg : subscription)

Save the subscription model in any “graphql.normal” file

You can give any name you would like to. This tutorial follows this naming convention.

step 2 : configure server

Setup the app.module.ts with two graphql modules, one for the normal server and another for the federation server. We need to configure the module in such a way that the only federation module loads the .graphql.federation file and only normal module loads the graphql.normal file. The .graphql file has to be loaded by both modules.

  imports: [
    GraphQLModule.forRoot({
      debug: false,
      playground: true,
      path: '/graphql',
      typePaths: ['./**/*.{graphql,graphql.normal}'],
      installSubscriptionHandlers: true,
    }),
    GraphQLFederationModule.forRoot({
      debug: false,
      playground: false,
      path: '/graphql-federated',
      typePaths: ['./**/*.{graphql,graphql.federation}'],
    }),
  ],

Notice that type paths for the two modules are different as per our convention. The normal graphql with subscription is now available at /grapqhl and the server for federation gateway is available at /graphql-federated

We are not spinning two servers here. Its the same express server with two middleware configured for different paths. So there will not be any performance issues

step 3 : the illusion

This is the most important step. There are some directives in graphql that only works in the federated mode and vice versa. You will finally end up in writing the custom version of the grapqhl model in the federated and the normal files. This will add the headache of duplicate graphql models in the application.

This problem can be tackled in a easy way , using dummy directives !

  • Define a declarative for “key”
import { SchemaDirectiveVisitor } from 'apollo-server-express';
import { GraphQLField } from 'graphql';

/**
 * This is a dummy implementation of the key directive for the normal mode
 */
export class FakeKeyDirective extends SchemaDirectiveVisitor {
  /**
   * Fake Key definition
   * @param _field Field of graphql
   */
  visitFieldDefinition(_field: GraphQLField<any, any>) {
    _field.args;
  }
}
  • Include it in the module
@Module({
  imports: [
    GraphQLModule.forRoot({
      debug: false,
      playground: true,
      path: '/graphql',
      typePaths: ['./**/*.{graphql,graphql.normal}'],
      installSubscriptionHandlers: true,

      directiveResolvers: {
        key: FakeKeyDirective,
      },
    }),
    GraphQLFederationModule.forRoot({
      debug: false,
      playground: false,
      path: '/graphql-federated',
      typePaths: ['./**/*.{graphql,graphql.federation}'],
    }),
  ],
  controllers: [AppController],
  providers: [AppService, UsersResolver, UserService],
})
export class AppModule {}
  • Define a fake implementation. This only has to work in the normal mode. So the file name has to end with “graphql.normal”
directive @key(fields: String) on OBJECT

Now you can define the model using the federation supported @key directive and the model works both in federation and normal graphql server.

type Department @key(fields: "id") {
  id: ID!
  name: String
}

thats all folks

Now you can start the federation gateway which listens to the the /graphql-federated and the federation works.

For subscription you can use any Websocket enabled gateways like nginx, istio etc and connect directly to the microservices

conclusion

Yes it is possible to enable federation and subscription for graphql in nestJS using a simple trick. More efficient than the Apollo method. You can download the entire code for your reference from my github repo (https://github.com/vinodsr/nestjs-graphql-federation-with-subscription).

bonus tip

Since you are using a separate file extension other than .graphql, your IDE won’t give the native graphql formatting for the graphql.normal and graphql.federation files. There is a fix for this. Just enabled file associations for those extensions.

This is how it looks like in VS Code. Adding the file extensions will give you syntax highlighting and code formatting for the normal and federation graphql.

Leave a Reply

Your email address will not be published. Required fields are marked *