5

How To Enable Subscription in Apollo Federation - DZone Web Dev

 2 years ago
source link: https://dzone.com/articles/enable-subscription-apollo-federation
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to Enable Subscription in Apollo Federation With NestJS GraphQL

By default, Apollo does not allow you to enable subscription in a federation server. Here's how to enable subscription in NestJS using Apollo GraphQL.

Join the DZone community and get the full member experience.

Join For Free

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 GraphQL. It supports both normal and federation mode. In federation mode, we can set up GraphQL servers in every microservice, 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 most powerful NodeJS frameworks available as of today. One of the cool features of NestJS is that it allows easy integration with databases, message queues, authentication, etc. Furthermore, it allows developers to create applications using NodeJS within a much stricter, robust, and flexible framework like Spring Boot/Angular.

The Problem

All this comes with some inherent caveats from the Apollo library. While setting up the federation, you cannot use a GraphQL subscription. 

You will probably see this error: Error message screenshot. 

Apollo had come up with an approach to overcome this problem, but it overcomplicates the situation. Let's 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

The 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 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 into different files 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 (e.g., extends)
  3. *.graphql.normal: for syntax supported only in normal mode (e.g., subscription)

Save the subscription model in any "graphql.normal" file.

Screenshot of GraphQL file.

Step 2: Configure the Server

Set up 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 only the federation module loads the .graphql.federation file and only the normal module loads the .graphql.normal file. The .graphql file has to be loaded by both modules.

TypeScript
  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 the type paths for the two modules are different as per our convention. The normal GraphQL with subscription is now available at /graphql and the server for the federation gateway is available at /graphql-federated.

We are not spinning two servers here. It is 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 work in the federated mode and vice versa. You will finally end up writing the custom version of the GraphQL 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 an easy way, using dummy directives!

  • Define a declarative for “key”
TypeScript
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.
TypeScript
@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
TypeScript
directive @key(fields: String) on OBJECT
  • Now you can define the model using the federation-supported @key directive, and the model works both in the federation and normal GraphQL server.
TypeScript
type Department @key(fields: "id") {  id: ID!  name: String }

Now you can start the federation gateway, which listens to 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 that is more efficient than the Apollo method. You can download the entire code for your reference from my Github repo.

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK