Graphql

快速开始

学习如何在NestJS中使用GraphQL,包括代码优先和模式优先两种方法

利用TypeScript和GraphQL的强大功能

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

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

您也可以构建自己的专用驱动程序(在这里了解更多)。

安装

首先安装所需的包:

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

# 对于Fastify和Apollo
# npm i @nestjs/graphql @nestjs/apollo @apollo/server@^4.12.2 @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()静态方法对其进行配置。

@@filename()
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),请传递以下选项:

@@filename()
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)。

警告 更新(2025年4月14日):默认的Apollo playground已被弃用,将在下一个主要版本中删除。相反,您可以使用GraphiQL,只需在GraphQLModule配置中设置graphiql: true,如下所示:

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

如果您的应用程序使用订阅,请确保使用graphql-ws,因为GraphiQL不支持subscriptions-transport-ws

代码优先

代码优先方法中,您使用装饰器和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类型都创建为接口。要生成类,请使用值为'class'outputAs属性。

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,
});

要将枚举生成为TypeScript联合类型而不是常规TypeScript枚举,请将enumsAsTypes选项设置为true

definitionsFactory.generate({
  // ...
  enumsAsTypes: 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/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);

提示 您必须在应用程序初始化后(在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集成

Fastify用户(在这里阅读更多)可以使用@nestjs/mercurius驱动程序,而不是使用Apollo。

@@filename()
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()方法接受一个选项对象作为参数。这些选项被传递给底层驱动程序实例。在这里阅读更多可用设置。

多个端点

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

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

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

第三方集成

示例

工作示例可在这里获得。