NestJS 9 GraphQL 中文文档(一) - 快速入门

528 阅读4分钟

写在前面的话

Nest 最新的版本为V9,较之前的V7/8有不小改动,GraphQL 部分之前的V8也是我翻译的,有很多不完善的地方,这次打算重新仔细翻译一遍,会持续更新这块内容,并最后贡献给中文文档仓库,为中文社区贡献一份力量。

利用 TypeScript 和 GraphQL 的强大能力

GraphQL 是一种功能强大的API查询语言,也是一种使用已有数据完成这些查询的运行时。这是一种优雅的方式,可以解决通常在REST API种发现的诸多问题。作为背景知识,我们建议你阅读这篇关于GraphQL和REST对比的文章。GraphQL与TypeScript相结合可以帮助你通过使用GraphQL查询语句开发出更好的安全类型查询,为你提供端到端的类型验证。

在本章中,我们假定你已经对GraphQL有了基本的了解,所以我们只关注如何使用内置的@nestjs/graphql 模块进行开发。GraphQLModule可以配置为使用Apollo服务(使用@nestjs/apollo驱动程序)或Mercurius服务(使用@nestjs/mercurius驱动程序)。我们给这些久经考验的GraphQL包提供官方集成方案,提供了一个简单的方式来使用GraphQL和Nest。另外你也可以构建自己专用的驱动程序(参考此处)。

安装

先从安装依赖包开始:

# For Express and Apollo (default)
$ npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express

# For Fastify and Apollo
# npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-fastify

# For Fastify and Mercurius
# npm i @nestjs/graphql @nestjs/mercurius graphql mercurius fastify

警告 @nestjs/graphql@>9@nestjs/apollo^10这两个包兼容Apollo v3(查阅Apollo Server 3 迁移指南获取更新信息),但是@nestjs/graphql@^8只支持Apollo v2(例如:apollo-server-express@2.x.x这个包)。

概览

Nest提供了两种构建GraphQL应用的方式,即代码优先模式优先。你需要选择一种最适合你的方式来进行开发。GraphQL这一章中的大部分章节都将被分为两个主要部分:如果你采用代码优先,则要遵循其中一个,如果你采用模式优先,则应遵循另外一个。

代码优先方式中,你将使用装饰器和TypeScript 类来生成相应的 GraphQL schema。如果你更喜欢用TypeScript并想避免在不同语言语法之间的上下文切换,那么这种方式会更有效。

模式优先方式中,真相来源就是GraphQL SDL(Schema Definition Language)文件。SDL是一种在不同平台间共享模式文件的与语言无关的方式。Nest会基于GraphQL schemas自动生成TypeScript定义(不管是用类或者接口),这样就能减少冗余模版代码的编写。

开始使用GraphQL & TypeScript

提示 在接下来的章节中,我们将集成@nestjs/apollo包。如果你想用mercurius包替代,请查阅[此节](to reduce the need to write redundant boilerplate code.)。

安装好依赖包之后,我们就可以导入GraphQLModule并使用forRoot()静态方法来配置它。

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
    }),
  ],
})
export class AppModule {}

提示 对于mercurius的集成,你应该使用MercuriusMercuriusDriverConfig替代。它们都导出自@nestjs/mercurius包。

forRoot()方法有一个可选对象作为参数。这些配置项被传递到底层驱动实例(在这里阅读更多的设置:ApolloMercurius)。例如,如果你想要禁用playground并关闭debug模式(Apollo专用),传递以下配置项:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      debug: false,
      playground: false,
    }),
  ],
})
export class AppModule {}

在此例中,这些配置项将被转发给ApolloServer构造器。

GraphQL palyground

palyground是一个图形化、可交互、嵌入浏览器内的GraphQL IDE,和GraphQL服务本身的默认URL是一样的。要使用playground,你需要配置并运行一个基础的GraphQL服务。要现在看,你可以安装并构建这个示例项目。或者,如果你跟着这些代码样例学习,一旦你完成了解析器一章,你就能进入playground了。

到时候,只要将你的程序运行起来,你就可以打开浏览器并导航到http://localhost:3000/graphql(主机名和端口可能根据你自己的配置有所不同)。你将看到GraphQL playground,如下所示。

注意 @nestjs/mercurius集成方案中不包含内置的GraphQL Playground的集成。你可以使用GraphiQL(设置graphiql: true)替代。

多端点

@nestjs/graphql模块的另一个非常有用的特性是可以同时启动多个端点。这能让你自己决定哪个端点将包含哪些模块。默认情况下,GraphQL会在整个应用程序中搜索解析器。要限制该扫描仅在部分模块中进行,使用include属性。

GraphQLModule.forRoot({
  include: [CatsModule],
}),

警告 如果你在一个单体应用中使用apollo-server-fastify包和多GraphQL多端点,请确保在GraphQLModule配置中打开disableHealthCheck设置。

代码优先

代码优先方式中,你使用装饰器和TypeScript类来生成对应的GraphQL schema。

要使用代码优先方式,首先要在配置项对象中添加autoSchemaFile属性:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),

autoSchemaFile属性的值就是创建自动生成的schema的路径。另外,schema也可以在内存中即时生成。要启用这个特性,设置autoSchemaFile属性为true即可:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: true,
}),

默认情况下,在生成的schema中的类型将按照它们在包含的模块中定义的顺序排序。要按字典顺序对schema进行排序,设置sortSchema属性为true即可:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
  sortSchema: true,
}),

例子

此处提供了一个完整可用的代码优先示例。

模式优先

要使用模式优先方式,首先要给配置项对象添加一个typePath属性。typePath属性指定了GraphQLModule应该在哪里查找你将要编写的GraphQL SDL schema 定义文件。这些文件将在内存中合成;这允许你把schemas分割成不同的文件并将它们放置在靠近自身解析器的地方。

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  typePaths: ['./**/*.graphql'],
}),

你通常还需要有GraphQL SDL类型对应的TypeScript定义(类和接口)。通过手工创建相应的TypeScript定义是多余和乏味的,那样做会让我们背离单一的真相来源——SDL中所做的每项更改都迫使我们也要再次调整TypeScript定义。要解决这个问题,@nestjs/graphql包可以从抽象语法书(AST)中自动生成TypeScript定义。要开启这个功能,需要在配置GraphQLModule的时候添加definitions选项属性。

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  typePaths: ['./**/*.graphql'],
  definitions: {
    path: join(process.cwd(), 'src/graphql.ts'),
  },
}),

definitions对象中的路径属性指定在哪里保存生成的TypeScript输出。默认情况下,所有生成的TypeScript类型将被创建为接口。要改为生成类,需要指明outputAs属性的值为'class'

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  typePaths: ['./**/*.graphql'],
  definitions: {
    path: join(process.cwd(), 'src/graphql.ts'),
    outputAs: 'class',
  },
}),

上述方式中每当应用程序启动时就会动态生成TypeScript定义。另外,最好构建一个简单的脚本来按需生成这些。例如,假设我们创建以下的脚本为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',
});

现在你可以按需运行该脚本:

$ ts-node generate-typings

提示 你可以预先编译脚本(例如:使用tsc)并使用node来执行它。

要为脚本开启观察模式(当任何 .graphql文件更改时自动生成类型),需传入watch选项到generate() 方法中。

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

要为每个对象类型自动生成额外的__typename字段,需开启emitTypenameField选项。

definitionsFactory.generate({
  // ...,
  emitTypenameField: true,
});

要将解析器(查询、突变、订阅)生成不带参数的普通字段,需开启skipResolverArgs选项。

definitionsFactory.generate({
  // ...,
  skipResolverArgs: true,
});

Apollo Sandbox

要用Apollo Sandbox替代graphql-playground作为你本地开发的GraphQL IDE,使用以下配置:

import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      playground: false,
      plugins: [ApolloServerPluginLandingPageLocalDefault()],
    }),
  ],
})
export class AppModule {}

例子

在此提供了一个完整可用的模式优先示例。

访问生成的schema

在某些情况下(比如端到端测试),你可能希望引用生成的schema对象。在端到端测试中,你可以使用graphql对象运行查询,而无需使用任何HTTP监听器。

使用GraphQLSchemaHost类,你就可以访问生成的schema(不管是在代码优先或模式优先方式中):

const { schema } = app.get(GraphQLSchemaHost);

提示 你必须在程序已经初始化之后(在onModuleInit钩子被app.listen()app.init() 方法触发后)调用GraphQLSchemaHost#schema 的 getter。

异步配置

当你需要改用异步地而不是静态地传递模块选项时,使用forRootAsync()方法即可。和大多数动态模块一样,Nest提供了集中处理异步配置的技术。

其中一种技术就是使用工厂函数:

 GraphQLModule.forRootAsync<ApolloDriverConfig>({
  driver: ApolloDriver,
  useFactory: () => ({
    typePaths: ['./**/*.graphql'],
  }),
}),

与其他工厂提供者一样,我们的工厂函数也可以是异步的,并可以通过inject注入依赖项。

GraphQLModule.forRootAsync<ApolloDriverConfig>({
  driver: ApolloDriver,
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    typePaths: configService.get<string>('GRAPHQL_TYPE_PATHS'),
  }),
  inject: [ConfigService],
}),

另外,你也可以改用类替代工厂来配置GraphQLModule,如下所示:

GraphQLModule.forRootAsync<ApolloDriverConfig>({
  driver: ApolloDriver,
  useClass: GqlConfigService,
}),

上述构造器在GraphQLModule中实例化类GqlConfigService,使用它来创建选项对象。注意在此例中,GqlConfigService必须实现GqlOptionsFactory接口,如下所示。GraphQLModule将在提供的类的实例化对象上调用createGqlOptions()方法。

@Injectable()
class GqlConfigService implements GqlOptionsFactory {
  createGqlOptions(): ApolloDriverConfig {
    return {
      typePaths: ['./**/*.graphql'],
    };
  }
}

如果你希望重用一个现有的选项提供者而不是在GraphQLModule中创建私有副本,可使用useExisting语法。

GraphQLModule.forRootAsync<ApolloDriverConfig>({
  imports: [ConfigModule],
  useExisting: ConfigService,
}),

Mercurius集成方案

除了使用Apollo,Fastify用户(在此了解更多信息)也可以使用@nestjs/mercurius驱动程序。

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius';

@Module({
  imports: [
    GraphQLModule.forRoot<MercuriusDriverConfig>({
      driver: MercuriusDriver,
      graphiql: true,
    }),
  ],
})
export class AppModule {}

提示 当应用程序运行时,打开浏览器并导航到http://localhost:3000/graphiql。你就应该看到这个GraphQL IDE

forRoot() 方法接口一个选项对象作为参数。这些选项会被传递到底层驱动程序的实例。在此了解更多可用的设置。