NestJS基础之GraphQL

1,256 阅读2分钟

学习 @nestjs/graphql

依赖

  • @nestjs/graphql 是一个单独的npm包
  • graphql-tools
  • graphql
  • apollo-server-express
  • apollo-server-fastify

GraphQLModule 与 forRoot()

Nestjs 与 GraphQL 的联系是通过 GraphQLModule 进行连接,GraphQLModule 中的 forRoot 中的参数就是传递给 ApolloServer 的相关的蚕食

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';

@Module({
  imports: [
    GraphQLModule.forRoot({}),
  ],
})
export class AppModule {}

下面我们要思考一个问题,就是我们在写 GraphQL 代码的时候第一原则是什么?

  • 统统使用 TypeScript 代码
  • 使用 GraphQL 的 原生schema

下面就是关于 GraphQL 代码写建议

代码优先

什么是代码优先呢?就是我们如果习惯了用 TypeScript 写代码,我们可以通过 TypeScript 映射到 GraphQL 的 Schema 文件。代码优先的好处是,我们可以统一使用TypeScript 语言来写代码。在 NestJS 中给我们提供了自动生成器,直接在 GraphQLModule.forRoot({}) 配置项中添加 autoSchemaFile 自动生成 Schema 文件到指定路径。

GraphQLModule.forRoot({
  autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),

架构 schema 优先

shema 优先的原则:直接写 graphql 文件,我们识别graphql也特别的简单, 配置 typePaths 到指定的文件路径。

GraphQLModule.forRoot({
  typePaths: ['./**/*.graphql'],
}),

schema 优先在编写代码时是不够的,我们还需要类型的定义来约束(类型和接口)我们的 TypeScript 代码。NestJS 也已经解决了这个问题,也是能直接生成,生成有两种方法。

  • 自动
GraphQLModule.forRoot({
  typePaths: ['./**/*.graphql'],
  definitions: {
    path: join(process.cwd(), 'src/graphql.ts'),
    outputAs: 'class',
  },
}),
  • 手动
// generate-typings.ts
import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
import { join } from 'path';

const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
  typePaths: ['./src/**/*.graphql'],
  path: join(process.cwd(), 'src/graphql.ts'),
  outputAs: 'class',
});

GraphQL Resolver

其实 Resolvers 就相当于 Controllers,只是思想不一样,底层不一样。

  • Resolver 本身是一个被 Resolver 修饰的类,类的内容就是我们要进行的相关 GraphQL 查询的操作,这些操作包含:
    • Query 查询,使用 Query 修饰器修饰
    • Mutation 变异,使用 Mutation 修饰器修饰
    • Subscription 订阅,使用 Subscription 修饰器修饰

在 Resolver 内部,其实就是根据参数,做响应的逻辑处理。

Resolver 内部类知识一个函数名,在查询时直接使用,和传递对应的参数

import { NotFoundException } from '@nestjs/common';
import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'apollo-server-express';
import { NewRecipeInput } from './dto/new-recipe.input';
import { RecipesArgs } from './dto/recipes.args';
import { Recipe } from './models/recipe';
import { RecipesService } from './recipes.service';

const pubSub = new PubSub();

@Resolver(of => Recipe)
export class RecipesResolver {
  constructor(private readonly recipesService: RecipesService) {}

  @Query(returns => Recipe)
  async recipe(@Args('id') id: string): Promise<Recipe> {
    const recipe = await this.recipesService.findOneById(id);
    if (!recipe) {
      throw new NotFoundException(id);
    }
    return recipe;
  }

  @Query(returns => [Recipe])
  recipes(@Args() recipesArgs: RecipesArgs): Promise<Recipe[]> {
    return this.recipesService.findAll(recipesArgs);
  }

  @Mutation(returns => Recipe)
  async addRecipe(
    @Args('newRecipeData') newRecipeData: NewRecipeInput,
  ): Promise<Recipe> {
    const recipe = await this.recipesService.create(newRecipeData);
    pubSub.publish('recipeAdded', { recipeAdded: recipe });
    return recipe;
  }

  @Mutation(returns => Boolean)
  async removeRecipe(@Args('id') id: string) {
    return this.recipesService.remove(id);
  }

  @Subscription(returns => Recipe)
  recipeAdded() {
    return pubSub.asyncIterator('recipeAdded');
  }
}

参考