2022 年 8 月 29 日

GraphQL、Prisma 和 React 的端到端类型安全:GraphQL API

14 分钟阅读

在本系列中,您将学习如何使用 React、GraphQL、Prisma 和其他一些有用的工具将这三者结合起来,实现端到端类型安全。

hero image prisma

目录

简介

在本节中,您将以前一篇文章中设置的项目为基础,扩展 GraphQL API 的功能。

在构建此 API 时,您将专注于确保与数据库的交互、解析器中的数据处理以及数据响应都是类型安全的,并且这些类型是同步的。

如果您错过了本系列的第一部分,以下是您将在此应用程序中使用的一些技术以及一些先决条件的快速概述。

您将使用的技术

以下是您将在本系列中使用的主要工具

  • Prisma 作为对象关系映射器 (ORM)
  • PostgreSQL 作为数据库
  • Railway 用于托管您的数据库
  • TypeScript 作为编程语言
  • GraphQL Yoga 作为 GraphQL 服务器
  • Pothos 作为代码优先的 GraphQL 模式构建器
  • Vite 用于管理和搭建您的前端项目
  • React 作为前端 JavaScript 库
  • GraphQL Codegen 用于根据 GraphQL 模式为前端生成类型
  • TailwindCSS 用于应用程序的样式设计
  • Render 用于部署您的 API 和 React 应用程序

假定知识

虽然本系列将尝试从初学者的角度详细介绍所有内容,但以下知识将有所帮助

  • JavaScript 或 TypeScript 的基本知识
  • GraphQL 的基本知识
  • React 的基本知识

开发环境

要跟随提供的示例进行操作,您需要具备

启动 GraphQL 服务器

构建 GraphQL API 所需的第一件事是运行中的 GraphQL 服务器。在本应用程序中,您将使用 GraphQL Yoga 作为您的 GraphQL 服务器。

安装 @graphql-yoga/nodegraphql 包以开始使用

安装这些包后,您现在可以启动自己的 GraphQL 服务器。前往 src/index.ts。将现有内容替换为此代码段

上面的代码执行以下操作

  1. 从 GraphQL Yoga 导入 createServer 函数
  2. 创建一个变量来保存 API 的端口,如果环境中不存在端口,则默认为 4000
  3. 创建 GraphQL 服务器的实例
  4. 在端口 4000 上启动服务器,并让控制台知道它正在运行

如果您启动服务器,您将可以访问正在运行的(空) GraphQL API

注意:GraphQL 服务器已启动并正在运行,但由于您尚未定义任何查询或 mutation,因此它尚不可用。

设置模式构建器

GraphQL 使用强类型模式来定义用户如何与 API 交互以及应返回哪些数据。构建 GraphQL 模式有两种不同的方法:代码优先和 SDL 优先

  • 代码优先:您的应用程序代码定义并生成 GraphQL 模式
  • SDL 优先:您手动编写 GraphQL 模式

在本应用程序中,您将使用名为 Pothos 的流行模式构建器,采用代码优先方法。

要开始使用 Pothos,您首先需要安装核心包

接下来,创建 Pothos 模式构建器的实例作为可共享模块。在 src 文件夹中,创建一个名为 builder.ts 的新文件,用于保存此模块

现在,从 @pothos/core 包导入默认导出,并导出名为 builder 的实例

定义 Date 标量类型

默认情况下,GraphQL 仅支持有限的标量数据类型集

  • Int
  • Float
  • String
  • Boolean
  • ID

但是,如果您回想一下您的 Prisma 模式,您会记得定义了一些字段,这些字段使用了 DateTime 数据类型。要在您的 GraphQL API 中处理这些类型,您需要定义自定义的 Date 标量类型。

幸运的是,由于开源社区的贡献,预制的自定义标量类型定义是可用的。您将使用的那个名为 graphql-scalars

您需要向模式构建器注册 Date 标量,以使其知道如何处理日期。模式构建器接受一个 泛型,您可以在其中指定各种配置

进行以下更改以注册 Data 标量类型

以下是上面代码段中的更改。您

  1. 导入 Date 标量类型的解析器,该解析器处理在您的 API 中将值转换为正确的日期类型
  2. 使用 SchemaBuilderScalars 配置注册了一个名为 "Date" 的新标量类型,并配置了在访问和验证此类型的字段时要使用的 JavaScript 类型
  3. 通过提供导入的 DateResolver,让构建器知道如何处理定义的 Date 标量类型

现在,在您的 GraphQL 对象类型和解析器中,可以使用 Date 标量类型。

添加 Pothos Prisma 插件

接下来,您需要定义 GraphQL 对象类型。这些类型定义了您的 API 将通过查询公开的对象和字段。

Pothos 为 Prisma 提供了一个出色的插件,该插件使此过程更加顺畅,并在您的 GraphQL 类型和数据库模式之间提供类型安全。

注意可以在不使用插件的情况下以类型安全的方式将 Pothos 与 Prisma 一起使用,但是该过程非常手动。有关详细信息,请参见此处

首先,安装插件

此插件提供了一个 Prisma 生成器,用于生成 Pothos 所需的类型。将生成器添加到 prisma/schema.prisma 中的 Prisma 模式中

添加完成后,您将需要一种生成 Pothos 工件的方法。在本系列稍后部署此应用程序时,您将需要安装此 API 的 node 模块并重新生成 Prisma Client,因此请继续在 package.json 中创建一个新的 script 来处理此操作

现在,您可以运行该命令来安装您的 node 模块并重新生成 Prisma Client 和 Pothos 输出

当您运行上面的命令时,您应该看到 Prisma Client 和 Pothos 集成都已生成。

现在,这些类型已生成,请前往 src/builder.ts。在这里,您将导入 PrismaPlugin 和生成的 Pothos 类型,并将它们应用于您的构建器

一旦添加生成的类型,您将注意到在 SchemaBuilder 的实例化中发生了 TypeScript 错误。

Pothos 非常智能,它知道,由于您正在使用 Prisma 插件,因此您需要向构建器提供 prisma 实例。Pothos 使用此实例来推断有关 Prisma Client 中类型的信息。在下一步中,您将创建该实例并将其添加到构建器中。

现在,在构建器实例中注册 Prisma 插件和生成的类型,以使 Pothos 了解它们

您将再次在此处看到 TypeScript 错误。这是因为 builder 现在期望将 Prisma Client 的实例提供给该函数。

在下一步中,您将实例化 Prisma Client 并在此处的 builder 中提供它。

创建 Prisma Client 的可重用实例

现在,您需要创建一个 Prisma Client 的可重用实例,该实例将用于查询您的数据库,并提供上一步中构建器所需的类型。

src 文件夹中创建一个名为 db.ts 的新文件

在该文件中,导入 Prisma Client 并创建名为 prisma 的客户端实例。导出该实例化的客户端

prisma 变量导入 src/builder.ts 并将其提供给 builder 以消除 TypeScript 错误

Pothos Prisma 插件现在已完全配置并可以使用了。这使用了 Prisma 生成的类型,并允许您在 GraphQL 对象类型和查询中轻松访问这些类型。

这样做的好处是,您现在拥有一个单一的事实来源(Prisma 模式),用于处理数据库中的类型、用于查询数据库的 API 以及 GraphQL 模式。

接下来,您将看到实际效果!

定义您的 GraphQL 类型

此时,您将使用配置了 Prisma 插件的构建器定义 GraphQL 对象类型。

注意:当您已经在 Prisma 模式中定义了数据形状时,手动定义 GraphQL 对象类型似乎是多余的。Prisma 模式定义了数据库中数据的形状,而 GraphQL 模式定义了 API 中可用的数据。

src 中创建一个名为 models 的新文件夹。然后在该新文件夹中创建一个 User.ts 文件

您将在此处定义 User 对象类型及其相关查询,这些查询将通过您的 GraphQL API 公开。导入 builder 实例

由于您正在使用 Pothos 的 Prisma 插件,因此 builder 实例现在有一个名为 prismaObject 的方法,您将使用该方法来定义您的对象类型。

该方法接受两个参数

  1. name:此新类型表示的 Prisma 模型的名称
  2. options:要定义的类型的配置

使用该方法创建 "User" 类型

注意:如果您在键入 name 字段之前在空引号集中按 Ctrl + 空格键,您应该会获得一些不错的自动完成功能,其中包含来自 Prisma 模式的可用模型列表,这要归功于 Prisma 插件。

options 对象中,添加一个 fields 键,该键使用 Pothos 的 "expose" 函数定义 idnamemessages 字段

注意:当您开始键入字段名称时,按 Ctrl + 空格键 将为您提供目标模型中与您正在使用的“expose”函数的数据类型匹配的字段列表。

上面的函数定义了一个 GraphQL 类型定义,并将其注册到 builder 实例中。从 builder 生成模式实际上不会在您的文件系统中存储您可以签出的 GraphQL 模式,但是您的 User 的结果类型定义将如下所示

接下来,在同一文件夹中添加另一个文件,名为 Message.ts

此文件将类似于 User.ts 文件,不同之处在于它将定义 Message 模型。

定义 idbodycreatedAt 字段。请注意,createdAt 字段在您的 Prisma 模式中具有 DateTime 类型,并且需要自定义配置来定义您定义的自定义 date 标量类型

此函数将生成以下 GraphQL 对象类型

实现您的查询

目前,您已经为您的 GraphQL 模式定义了对象类型,但是您尚未定义实际访问该数据的方式。为此,您首先需要初始化一个 Query 类型

在您的 src/builder.ts 文件底部,使用 builderqueryType 函数初始化 Query 类型

这会注册一个特殊的 GraphQL 类型,该类型保存每个查询的定义,并充当 GraphQL API 的入口点。您在 builder.ts 文件中定义此类型,以确保查询构建器定义了 Query 类型,这样您就可以稍后向其添加查询字段。

在此 queryType 函数中,您可以直接添加查询定义,但是,您将在代码库中单独定义这些定义,以更好地组织您的代码。

prisma 实例导入 src/models/User.ts

然后,使用 builderqueryField 函数,定义一个 "users" 查询,该查询公开您定义的 User 对象类型

上面的代码段

  1. 向 GraphQL 模式的 Query 类型添加一个名为 "users" 的字段
  2. 定义一个字段,该字段解析为 Prisma 模式中的某种类型
  3. 让 Pothos 知道此字段将解析为 Prisma Client 的 User 类型的数组
  4. 为此字段设置解析器函数。

注意resolve 函数的 query 参数位于参数列表的开头。这是 Pothos 在使用 prismaField 函数时填充的特定字段,用于以高性能的方式加载数据和关系。如果您来自 GraphQL 背景,这可能会令人困惑,因为它更改了参数的预期顺序。

为了更好地可视化所发生的事情,以下是 Query 类型和 users 查询,它们将由本节中的代码生成

应用 GraphQL 模式

现在,您已经定义并实现了所有 GraphQL 对象类型和查询。最后需要的是一种在单个位置注册所有这些类型并基于您的配置生成 GraphQL 模式的方法。

src 中创建一个名为 schema.ts 的新文件

此文件将仅导入模型,从而导致运行文件中的代码,并运行 builder 实例的 toSchema 函数以生成 GraphQL 模式

toSchema 函数生成 GraphQL 模式的抽象语法树 (AST) 表示形式。在下面,您可以看到 AST 和 GraphQL 表示形式的外观

在您的 src/index.ts 文件中,导入您刚刚创建的 schema 变量。createServer 函数的配置对象采用名为 schema 的键,该键将接受生成的 GraphQL 模式

太棒了!您的 GraphQL 模式已使用代码优先方法定义,您的 GraphQL 对象和查询类型与您的 Prisma 模式模型同步,并且您的 GraphQL 服务器正在接收生成的 GraphQL 模式。

此时,运行服务器,以便您可以试用 API

运行上述命令后,在浏览器中打开 https://127.0.0.1:4000/graphql 以访问 GraphQL playground。您应该看到一个看起来像这样的页面

在屏幕的左上角,点击Explorer按钮以查看 API 的可用查询和 mutation

如果您单击users查询类型,则屏幕右侧将自动填充用户数据的查询。

点击“执行查询”按钮运行该查询以查看 API 的实际效果

随意试用不同的选项,以选择您要查询的字段以及您要包含的“messages”关系中的哪些数据。

总结和后续步骤

在本文中,您构建了整个 GraphQL API。API 的构建方式是类型安全的,它利用了 Prisma 生成的类型。这些类型与 Pothos Prisma 插件一起,使您能够确保 ORM、GraphQL 对象类型、GraphQL 查询类型和解析器中的类型都与数据库模式同步。

在此过程中,您

  • 使用 GraphQL Yoga 设置了 GraphQL 服务器
  • 设置了 Pothos 模式构建器
  • 定义了您的 GraphQL 对象和查询类型
  • 使用 Prisma Client 查询了数据

在下一篇文章中,您将通过设置代码生成来保持前端客户端和 API 上的类型同步来完成收尾工作。然后,您将部署您完成的应用程序!

不要错过下一篇文章!

注册 Prisma 新闻通讯