Skip to content

Latest commit

 

History

History
259 lines (214 loc) · 5.44 KB

README.md

File metadata and controls

259 lines (214 loc) · 5.44 KB

If you want to use Mercurius with @nestjs/graphql > v10, please use the @nestjs/mercurius package

Nestjs Mercurius

Use Mercurius GraphQL with Nestjs framework

Visit the Wiki

Install

npm i @nestjs/platform-fastify fastify mercurius nestjs-mercurius

Use

Register the module

import { Module } from '@nestjs/common';
import { MercuriusModule } from 'nestjs-mercurius';

@Module({
  imports: [
    // Work also with async configuration (MercuriusModule.forRootAsync)
    MercuriusModule.forRoot({
      autoSchemaFile: true,
      context: (request, reply) => ({
        user: request.user,
      }),
      subscription: {
        context: (connection, request) => ({
          user: request.user,
        }),
      },
    }),
  ],
  providers: [
    CatResolver,
  ],
})
export class AppModule {}

The Object type

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

@ObjectType()
export class Cat {
  @Field(() => ID)
  id: number;
  
  @Field()
  name: string;
  
  @Field(() => Int)
  ownerId: number;
}

The Resolver

import { Resolver, Query, ResolveField, Parent, Mutation, Subscription, Context, Args } from '@nestjs/graphql';
import { ParseIntPipe } from '@nestjs/common';
import { ResolveLoader, toAsyncIterator, LoaderQuery } from 'nestjs-mercurius';
import { PubSub } from 'mercurius';
import { Cat } from './cat';

@Resolver(() => Cat)
export class CatResolver {
  constructor(
    private readonly catService: CatService,
    private readonly userService: UserService,
  ) {}

  @Query(() => [Cat])
  cats(@Args({name: 'filter', type: () => String, nullable: true}) filter?: string) {
    return this.catService.find(filter);
  }

  @Query(() => Cat, { nullable: true })
  cat(@Args('id', ParseIntPipe) id: number) {
    return this.catService.findOne(id);
  }

  @Mutation(() => Cat)
  createCat(
    @Args('name') name: string,
    @Context('pubsub') pubSub: PubSub,
    @Context('user') user: User,
  ) {
    const cat = new Cat();
    cat.name = name;
    cat.ownerId = user.id;
    //...
    pubSub.publish({
      topic: 'CatCreated',
      payload: { cat },
    });
    return cat;
  }
  
  @Subscription(() => Cat, {
    resolve: (payload) => payload.cat,
    filter: (payload, vars, context) =>
      payload.cat.ownerId !== context.user.id,
  })
  onCatCreated(
    @Context('pubsub') pubSub: PubSub,
  ) {
    return toAsyncIterator(pubSub.subscribe('CatCreated'));
  }
  
  @ResolveField(() => Int)
  age(@Parent() cat: Cat) {
    return 5;
  }
  
  @ResolveLoader(() => User, { opts: { cache: false } })
  owner(
    @Parent() queries: LoaderQuery<Cat>[],
  ) {
    return this.userService.findById(
      // queries is an array of objects defined as { obj, params } where obj is the current object and params are the GraphQL params
      queries.map(({ obj }) => obj.ownerId)
    );
  }
}

Federation

Install necessary dependencies

npm i @apollo/federation

The Gateway

import { Module } from '@nestjs/common';
import { MercuriusGatewayModule } from 'nestjs-mercurius';

@Module({
  imports: [
    MercuriusGatewayModule.forRoot({
      graphiql: 'playground',
      subscription: true,
      gateway: {
        pollingInterval: 10000,
        services: [
          {
            name: 'users',
            url: 'https://....',
            wsUrl: 'wss://...',
          },
          {
            name: 'pets',
            url: 'https://...',
            rewriteHeaders: headers => headers,
          },
        ],
      },
    }),
  ],
})
export class GatewayModule {}

The Service

import { Module } from '@nestjs/common';
import { MercuriusModule } from './mercurius.module';
import { User } from './user';
import { PetResolver, UserResolver } from './resolvers';

@Module({
  imports: [
    MercuriusModule.forRoot({
      autoSchemaFile: true,
      federationMetadata: true,
      buildSchemaOptions: {
        orphanedTypes: [User],
      },
      //...
    }),
  ],
  providers: [
    PetResolver,
    UserResolver,
  ],
})
export class PetModule {}

The Resolver

import { Resolver, ResolveReference } from '@nestjs/graphql';
import { Pet } from './pet';
import { Reference } from './reference.interface';

@Resolver(() => Pet)
export class PetResolver {
  constructor(
    private readonly petService: PetService,
  ) {}

  @ResolveReference()
  resolveReference(ref: Reference<'Pet', 'id'>) {
    return this.petService.findOne(ref.id);
  }
}

Resolve reference could also be defined as Loader, potentially improving performance:

import { ResolveReferenceLoader } from './resolve-reference-loader.decorator';
import { LoaderQuery } from './loader.interface';

@Resolver(() => Pet)
export class PetResolver {
  constructor(
    private readonly petService: PetService,
  ) {}

  @ResolveReferenceLoader()
  resolveReference(refs: LoaderQuery<Reference<'Pet', 'id'>>) {
    return this.petService.findById(
      refs.map(({ obj }) => obj.id)
    );
  }
}

Hooks

Register mercurius hooks as service methods, using the @GraphQLHook() decorator

import { GraphQLHook } from 'nestjs-mercurius';

@Injectable()
export class HookService {
  @GraphQLHook('preValidation')
  async onPreValidation(schema: GraphQLSchema, source: DocumentNode, context: any) {
   //...
  }
}