跳到主要内容

发挥 TypeScript 和 GraphQL 的威力

GraphQL 是一种用于 API 的强大查询语言,也是用现有数据实现这些查询的运行时。 它是一种解决 REST API 通常存在的许多问题的优雅方法。 有关背景信息,建议阅读 GraphQL 和 REST 之间的比较。 GraphQL 与 TypeScript 结合使用, 帮助您在 GraphQL 查询中实现更好的类型安全,为您提供端到端的类型支持。

在本章中,我们假设您对 GraphQL 有基本的了解,并专注于如何使用内置的 @nestjs/graphql 模块。 GraphQLModule 可以配置为使用 Apollo server(使用 @nestjs/apollo 驱动)和 Mercurius(使用 @nestjs/mercurius)。 我们为这些经过验证的 GraphQL 包提供了官方集成,以提供在 Nest 中使用 GraphQL 的简单方式 (在此处查看更多集成)。

您还可以构建自己的专用驱动程序(在此处阅读更多信息)。

安装

首先安装所需的软件包:

# 对于 Express 和 Apollo(默认)
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql

# 对于 Fastify 和 Apollo
# npm i @nestjs/graphql @nestjs/apollo @apollo/server @as-integrations/fastify graphql

# 对于 Fastify 和 Mercurius
# npm i @nestjs/graphql @nestjs/mercurius graphql mercurius
注意

@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 模式。 如果您更喜欢仅使用 TypeScript 并避免在语言语法之间切换上下文,此方法将很有用。

模式优先方法中,真相的源头是 GraphQL SDL(模式定义语言)文件。 SDL 是在不同平台之间共享模式文件的一种与语言无关的方式。 Nest 根据 GraphQL 模式自动生成 TypeScript 定义(使用类或接口),以减少编写冗余样板代码的需要。

开始使用 GraphQL 和 TypeScript

提示

在接下来的章节中,我们将集成 @nestjs/apollo 包。如果您想使用 mercurius 包, 请导航到这一节

安装完软件包后,我们可以导入 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 集成,您应该使用 MercuriusDriverMercuriusDriverConfig。 两者都是从 @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,
playground: false,
}),
],
})
export class AppModule {}

在这种情况下,这些选项将被转发到 ApolloServer 构造函数。

GraphQL playground

Playground 是一个图形化、交互式的、内置的 GraphQL IDE, 在与 GraphQL 服务器相同的 URL 上默认可用。要访问 Playground, 您需要配置并运行一个基本的 GraphQL 服务器。要立即查看它, 您可以安装并构建 这里的工作示例。 或者,如果您正在跟随这些代码示例,完成 解析器章节 中的步骤后, 您可以访问 Playground。

安装完毕,并在后台运行应用程序后,可以打开 Web 浏览器, 导航到 http://localhost:3000/graphql(主机和端口可能因您的配置而异)。 然后,您将看到下面显示的 GraphQL Playground。

备注

@nestjs/mercurius 集成不附带内置的 GraphQL Playground 集成。 相反,您可以使用 GraphiQL(设置 graphiql: true)。

多个端点

@nestjs/graphql 模块的另一个有用的功能是能够同时服务多个端点。 这使您可以决定哪些模块应该包含在哪个端点中。 默认情况下,GraphQL 会在整个应用程序中搜索解析器。 要将此扫描限制为仅某些模块,请使用 include 属性。

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

如果您在单个应用程序中使用 @apollo/server 与 @as-integrations/fastify 包处理多个 GraphQL 端点, 请确保在 GraphQLModule 配置中启用 disableHealthCheck 设置。

代码优先

在代码优先方法中,您使用装饰器和 TypeScript 类来生成相应的 GraphQL 模式。

要使用代码优先方法,请首先在选项对象中添加 autoSchemaFile 属性:

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

autoSchemaFile 属性值是自动生成的模式将被创建的路径。或者,模式可以在内存中即时生成。 要启用此选项,请将 autoSchemaFile 属性设置为 true

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

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

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

示例

一个完整的代码优先示例在这里

模式优先

要使用模式优先方法,请首先在选项对象中添加 typePaths 属性。 typePaths 属性指示 GraphQLModule 应在其中查找您将编写的 GraphQL SDL 模式定义文件的位置。 这些文件将在内存中合并;这允许您将模式拆分为几个文件并将其放置在其解析器附近。

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 对象的 path 属性指示要保存生成的 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 执行它。

要为脚本启用 watch 模式(在任何 .graphql 文件更改时自动生成 typings), 请将 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

要在本地开发的 GraphQL IDE 中使用 Apollo Sandbox 而不是 graphql-playground,请使用以下配置:

import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';

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

实例

一个完整的模式优先示例在这里

访问生成的模式

在某些情况下(例如端到端测试),您可能希望获取对生成的模式对象的引用。 在端到端测试中,然后您可以使用 graphql 对象运行查询,而无需使用任何 HTTP 监听器。

您可以访问生成的模式(无论是代码优先还是模式优先方法),使用 GraphQLSchemaHost 类:

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

必须在应用程序初始化后调用 GraphQLSchemaHost#schema 获取器(在通过 app.listen()app.init() 方法触发 onModuleInit 钩子后)。

异步配置

当需要异步传递模块选项而不是静态传递时,请使用 forRootAsync() 方法。 与大多数动态模块一样,Nest 提供了处理异步配置的几种技术。

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

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

与其他工厂提供程序一样,我们的工厂函数可以是async的,并且可以通过 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 集成

Fastify 用户可以选择使用 @nestjs/mercurius 驱动而不是 Apollo。

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() 方法采用选项对象作为参数。这些选项将传递到底层驱动程序实例。 在此处阅读有关可用设置的更多信息

第三方集成

GraphQL Yoga