概述
我们的GraphQL 服务器缺少一些类型安全。我们需要一种方法将我们的GraphQL 架构类型转换为可以传递给我们的ListingAPI
类和解析器函数的 TypeScript 类型。手动编写这些类型需要大量工作,而且很容易与我们的GraphQL 架构不同步。幸运的是,GraphQL Code Generator 可以帮助我们!
在本课程中,我们将
- 为我们的解析器函数和数据源生成服务器端类型
GraphQL Codegen
这GraphQL Code Generator读取GraphQL 架构并生成我们在整个服务器中都可以使用的 TypeScript 类型。它可以防止我们的 TypeScript 类型在更改架构时过时,使我们可以专注于开发架构,而不是不断更新类型定义!我们的架构应该是我们的GraphQL 服务器的真实来源,GraphQL Code Generator 使我们能够将它作为真实来源。
安装依赖项
让我们先导航到项目根目录的终端,然后安装三个开发依赖项:@graphql-codegen/cli
、@graphql-codegen/typescript
和@graphql-codegen/typescript-resolvers
。
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
接下来,让我们创建一个可以在我们的package.json
文件中运行的 codegen 命令。在scripts
对象下添加一个名为generate
的新条目,并将其设置为graphql-codegen
。
"scripts": {"compile": "tsc","dev": "ts-node-dev --respawn ./src/index.ts","start": "npm run compile && nodemon ./dist/index.js","test": "jest","generate": "graphql-codegen"},
要成功运行此命令,我们需要一个文件来包含 GraphQL Code Generator 可以遵循的指令。
这 codegen
文件
让我们在项目的根目录中创建一个名为codegen.ts
的文件。
📦 intro-typescript┣ 📂 src┣ 📄 codegen.ts┣ 📄 package.json┣ 📄 README.md┗ 📄 tsconfig.json
此文件将从@graphql-codegen/cli
导入CodegenConfig
,我们将使用它来设置所有 codegen 详细信息。
import type { CodegenConfig } from "@graphql-codegen/cli";const config: CodegenConfig = {};export default config;
我们可以在config
对象中设置的第一个属性是架构所在的目录。我们将传入相对于当前目录的文件路径。
const config: CodegenConfig = {schema: "./src/schema.graphql",};
接下来,我们定义了生成的类型应输出的位置。在名为generates
的键下,我们将添加一个包含我们所需的路径src/types.ts
的对象。这将在服务器的src
文件夹中创建一个名为types.ts
的新文件。
const config: CodegenConfig = {schema: "./src/schema.graphql",generates: {"./src/types.ts": {},},};
最后,让我们在plugins
键下告诉 Code Generator 使用哪些插件。这是一个包含typescript
和typescript-resolvers
的数组,用于指向我们的两个插件。
generates: {"./src/types.ts": {plugins: ["typescript", "typescript-resolvers"],},},
生成类型
我们拥有运行 codegen 命令所需的一切。在根目录中打开一个终端,然后运行以下命令。
npm run generate
片刻之后,我们应该看到一个新文件已添加到服务器的src
目录中:types.ts
!让我们来看看。
在文件底部,我们将找到type Resolvers
。此类型是通过查看架构中的对象生成的;我们目前只有两个对象,Query
和Listing
。
export type Resolvers<ContextType = any> = {Listing?: ListingResolvers<ContextType>;Query?: QueryResolvers<ContextType>;};
代码开始看起来有点复杂,但在幕后,TypeScript 已针对我们的架构进行了所有验证,为我们提供了可以用来表示以下内容的类型
- 进入每个解析器的数据类型
- 这些数据上的属性,这些属性是解析器可以访问的
- 每个解析器函数返回的数据类型
因此,我们可以使用这个Resolvers
类型为我们的resolvers
对象带来类型安全(以及一些有用的代码验证)。
回到resolvers.ts
中,我们将导入此类型并将其应用于我们的resolvers
对象。
import { Resolvers } from "./types";export const resolvers: Resolvers = {Query: {featuredListings: (_, __, { dataSources }) => {return dataSources.listingAPI.getFeaturedListings();},},};
这消除了我们的解析器中的红色波浪线错误!
在 listing-api.ts
中添加类型注释
我们的 codegen 输出负责为架构中的对象类型生成精确的 TypeScript 类型,为我们提供了一个Listing
类型,我们可以将其用于服务器代码。
/** A particular intergalactic location available for booking */export type Listing = {__typename?: "Listing";/** Indicates whether listing is closed for bookings (on hiatus) */closedForBookings?: Maybe<Scalars["Boolean"]["output"]>;/** The cost per night */costPerNight?: Maybe<Scalars["Float"]["output"]>;id: Scalars["ID"]["output"];/** The number of beds available */numOfBeds?: Maybe<Scalars["Int"]["output"]>;/** The listing's title */title: Scalars["String"]["output"];};
让我们回到我们的数据源文件:datasources/listing-api.ts
。在文件顶部,我们将从types.ts
导入Listing
类型。
import { Listing } from "../types";
使用此类型,我们可以使用Promise<Listing[]>
作为返回类型来注释getFeaturedListings
方法。我们还将更新传递给this.get
调用的变量类型,使其也变为Listing[]
。
getFeaturedListings(): Promise<Listing[]> {return this.get<Listing[]>("featured-listings");}
太好了!我们在listing-api.ts
文件中也已准备好类型安全。
现在,你可能会问自己,我们的 解析器 函数实际上是如何访问我们刚刚创建的 数据源 的?换句话说,我们如何填充他们接收到的第三个位置参数,contextValue
,使用我们想要使用的 ListingAPI
类?
好问题!答案是,我们实际上 还没有 连接我们服务器的这部分。我们将在下一课中解决这个问题!
练习
关键要点
- 使用 GraphQL 代码生成器,我们可以从架构 字段 生成类型,并在代码中直接使用它们。你好,类型安全!
- 要在我们的 GraphQL 架构 上运行代码生成,我们在项目中定义一个
codegen.ts
文件。 - 该
codegen.ts
文件至少应指定以下属性:schema
,指定 GraphQL 架构 的路径generates
,指定应输出生成类型的路径plugins
,列出应包含在代码生成过程中的插件
- 该
typescript
代码生成插件是根据 GraphQL 架构 生成 TypeScript 类型的基本插件。 - 该
typescript-resolvers
代码生成插件将增强代码生成过程,以包括一个Resolvers
类型,我们可以使用它来用单个、全面的类型来注释我们的 解析器。
接下来
架构、数据源 和 解析器?我们都有了。但它们还没有 一起 工作。如果我们运行我们的服务器,它将不知道 ListingAPI
——它也不知道我们的 解析器!让我们确保所有部分都知道彼此,并发送我们对 真实数据 的第一个查询,在下一课中。
分享您关于本课的问题和评论
此课程目前处于
您需要一个 GitHub 帐户才能在下方发布。还没有? 改为在我们的 Odyssey 论坛上发帖。