DEV Community

Joseph Sutton
Joseph Sutton

Posted on

Polyglot Microservices: Federated Subscriptions in Golang, Rust, and Node.js, Pt. 3

Series Navigation

  1. Golang Microservices (spells)
  2. Rust Microservice (messages)
  3. Node.js Microservice (players)
  4. Gateway

Player Subgraph (Node.js)

Now, let's create the Nest.js subgraph

nx add @nx/nest
nx g @nx/nest:app apps/player-service
Enter fullscreen mode Exit fullscreen mode

Let's add some dependencies:

pnpm i @nestjs/graphql graphql graphql-yoga @graphql-yoga/nestjs-federation 
Enter fullscreen mode Exit fullscreen mode

We're going to be using the GraphQL Yoga server here, because NestJS's federated Apollo configuration doesn't allow for subscriptions -- fun fact (mainly because Apollo charges for federated subscriptions, and we can get that for free if we use Hive Gateway over the Apollo Router).

If you need higher throughput on your router, Wundergraph's Router is free and it's in Golang. I've used it before, but my use-case only required Hive Gateway -- for now.

Anyhow, here's how we set up our src/app.module.ts with the GraphQL module configured for graphql-yoga:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import {
  YogaFederationDriver,
  YogaFederationDriverConfig,
} from '@graphql-yoga/nestjs-federation';
import { PlayerModule } from './player/player.module';

@Module({
  imports: [
    GraphQLModule.forRoot<YogaFederationDriverConfig>({
      driver: YogaFederationDriver,
      autoSchemaFile: {
        federation: {
          version: 2,
          // required for federated subscriptions
          importUrl: 'https://specs.apollo.dev/federation/v2.4',
        },
      },
      subscriptions: true,
    }),
    PlayerModule,
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

For our player module, we need our resolver in src/player/player.resolver.ts:

import { Args, Mutation, Resolver, Subscription } from '@nestjs/graphql';
import { Player } from './player.model';
import { createPubSub, Repeater } from 'graphql-yoga';

const pubSub = createPubSub();

@Resolver(() => Player)
export class PlayerResolver {
  @Mutation(() => Player)
  enterArea(
    @Args('playerId') playerId: string,
    @Args('area') area: string
  ): Player {
    pubSub.publish(`${area}`, { id: playerId });
    return { id: playerId };
  }

  @Subscription(() => Player, {
    nullable: true,
    resolve: (value: Player) => value,
  })
  nearbyPlayers(@Args('area') area: string): Repeater<Player> {
    return pubSub.subscribe(`${area}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, we need our entity that we're resolving in src/player/player.model.ts:

import { Field, ID, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class Player {
  @Field(() => ID)
  id: string;
}
Enter fullscreen mode Exit fullscreen mode

Let's tie it together in src/player/player.module.ts:

import { Module } from '@nestjs/common';
import { PlayerResolver } from './player.resolver';

@Module({
  providers: [PlayerResolver],
})
export class PlayerModule {}
Enter fullscreen mode Exit fullscreen mode

As with the above services, you'll boot this up and migrate to the proper URL and test it out with multiple browser tabs open:

subscription NearbyPlayers {
  nearbyPlayers(area: "forest") {
    id
  }
}
Enter fullscreen mode Exit fullscreen mode
mutation {
  enterArea(playerId: "1243", area: "forest"){
    id
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, let's move onto the final part: tying it all together with Hive Gateway in part 4!

Top comments (0)