从GraphQL模式生成类型
如何确保您的解析器类型安全
👋 如果您还没有设置使用 Apollo Server 的 TypeScript项目,请继续阅读我们的入门指南。
GraphQL 使用一个 类型系统 来清晰定义每种类型及 字段 可用的数据。基于此模式,类型生成库可以自动根据该模式生成 TypeScript 类型。
您可以使用由这些生成的 TS 类型在你的 解析器 中进行类型检查,以确保解析器返回值的类型与模式中指定的 字段类型一致。对解析器进行类型检查可以帮助您快速捕获错误,并让您相信类型安全可以确保错误的无缝处理。
想要为您的 Apollo Federation 打印子图 子图的类型?我们的 子图模板 铺垫可以帮助您快速设置带有生成类型的 子图
设置您的项目
我们将使用 GraphQL Code Generator 库基于我们的 GraphQL方案 来生成类型。为 GraphQL Code Generator 提供模式有多种方式。下面,我们将介绍最常见的方法,该方法需要我们的方案存储在 .graphql
文件中。
如果您还没有做的话,请将服务器方案移动到 .graphql
文件,如下所示:
type Query {books: [Book]}type Book {title: Stringauthor: String}type AddBookMutationResponse {code: String!success: Boolean!message: String!book: Book}type Mutation {addBook(title: String, author: String): AddBookMutationResponse}
如果您已将模式移动到一个 .graphql
文件中,请更新导入以确保您仍然正确地将模式传递给服务器。在创建服务器的文件中,您可以使用 readFileSync
从 fs
包读取模式:
// ...other importsimport { readFileSync } from 'fs';// Note: this uses a path relative to the project's// root directory, which is the current working directory// if the server is executed using `npm run`.const typeDefs = readFileSync('./schema.graphql', { encoding: 'utf-8' });interface MyContext {dataSources: {books: Book[];};}const server = new ApolloServer<MyContext>({typeDefs,resolvers,});// ... start our server
重新启动您的服务器以确保它可以找到和使用您的模式,以及确保一切按预期工作。接下来,我们将安装生成类型所需的包。
安装和配置依赖项
运行以下命令,将 @graphql-codegen/cli
, @graphql-codegen/typescript
和 @graphql-codegen/typescript-resolvers
包安装到项目的开发依赖项中:
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
关于上述每个软件包的更多信息,请查看GraphQL Code Generator文档。
接下来,我们将设置一个配置文件来告诉GraphQL Code Generator如何在何处以及如何生成类型。您可以通过手动创建一个codegen.yml
文件或通过使用以下命令来完成,该命令将指导您完成此过程:
npx graphql-code-generator init
以下是一个codegen.yml
文件的示例:
# This configuration file tells GraphQL Code Generator how# to generate types based on our schema.schema: "./schema.graphql"generates:# Specify where our generated types should live../src/__generated__/resolvers-types.ts:plugins:- "typescript"- "typescript-resolvers"config:useIndexSignature: truecontextType: "../index#MyContext"
查看文档以了解更多关于上述配置选项的信息。
最后,我们建议您添加有助于您的package.json
文件中添加有用的脚本,以确保您的 TS 类型定期生成:
{// ..."scripts": {"generate": "graphql-codegen --config codegen.yml","compile": "npm run generate && tsc","start": "npm run compile && node ./dist/index.js",},// ...}
我们还推荐添加脚本以监视您的代码, 启用您的类型在您工作后台进行再生,以及重新编译TypeScript文件。
以上,运行npm start
命令根据我们的GraphQL模式生成类型,并编译我们的TypeScript代码。第一次运行graphql-codegen命令时,您将在codegen.yml
文件中指定的路径看到充满生成类型的文件。
向解析器添加类型
typescript-resolvers插件创建了一个Resolvers类型,您可以使用它将类型添加到您的解析器映射中,确保您的解析器的返回值与模式中指定的字段类型匹配。
将Resolvers
类型导入到您定义解析器的文件中:
// This is the file where our generated types live// (specified in our `codegen.yml` file)import { Resolvers } from './__generated__/resolvers-types';
现在,您可以直接将Resolvers
类型添加到您的resolver映射中:
export const resolvers: Resolvers = {}
您的解析器现在可以对每个解析器的参数和返回值进行类型检查,以匹配模式:
export const resolvers: Resolvers = {Query: {// TypeScript now complains about the below resolver because// the data returned by this resolver doesn't match the schema type// (i.e., type Query { books: [Book] })books: () => {return "apple";},},}
如果你的解析器分布在多个文件中,你可以将这些解析器生成的对应类型提取到这些文件中。例如,下面,我们将生成的类型导入到我们为查询和更新的单独文件中:
import { QueryResolvers } from '__generated__/resolvers-types';// Use the generated `QueryResolvers`// type to type check our queries!const queries: QueryResolvers = {// ...queries};export default queries;
import { MutationResolvers } from '__generated__/resolvers-types';// Use the generated `MutationResolvers` type// to type check our mutations!const mutations: MutationResolvers = {// ...mutations};export default mutations;
解析器上下文类型
您还可以配置 GraphQL 代码生成器,以便为您的解析器共享的上下文添加一个类型,确保 TypeScript 在您尝试使用不存在值时发出警告。
为此,您必须首先将传递给 Apollo 服务器 的接口导出为一个泛型类型参数,用于您的上下文值类型:
export interface MyContext {dataSources: {books: Book[];};}const server = new ApolloServer<MyContext>({typeDefs,resolvers,});
还记得我们上面的 contextType
吗?您可以像下面这样将导出的上下文接口传递给 contextType
配置选项:
# ...config:useIndexSignature: true# Providing our context's interface ensures our# context's type is set for all of our resolvers.# Note, this file path starts from the location of the# file where you generate types.# (i.e., `/src/__generated__/resolvers-types.ts` above)contextType: "../index#MyContext"
一旦重新生成您的类型,您的上下文值现在在所有的 resolvers 中都将自动进行类型化:
const resolvers: Resolvers = {Query: {// Our third argument (`contextValue`) has a type here, so we// can check the properties within our resolver's shared context value.books: (_, __, contextValue) => {return contextValue.dataSources.books;},},}
基本可运行示例
查看我们使用 Apollo 服务器和生成的类型在 CodeSandbox 上的示例:
有关其支持的特性和集成方法的进一步指导,请查看 GraphQL 代码生成器的文档。