在本教程中,我们将引导你完成创建一个简单的 GraphQL 后端应用程序。
我们将使用 GQLoom
搭配你喜欢的 schema builder 来定义 GraphQL Resolver 和 Schema,构建一个简单的猫舍应用,能够查询猫舍中的猫并能给猫舍中添加新的猫。
你将使用以下技术:
GQLoom
:更简单、更高效地定义 GraphQL Schema 和解析器。你可以选择你熟悉的 Schema Builder,比如 Valibot、Zod,甚至直接使用 GraphQL.js。
在开始之前,请确保你已经安装了以下软件:
本教程假设你已经掌握 TypeScript、Node.js、GraphQL 的基础知识,并对 Valibot 或者 Zod 有一定认识。如果你是初学者,我们建议你先学习一下这些基础知识。
首先,我们需要创建一个新的 Node.js 项目。
打开你的命令行,运行以下命令:
在上面的命令中:我们创建了一个名为 cattery
的新目录,并进入该目录。然后,我们使用 npm init -y
命令初始化一个新的 Node.js 项目,并自动生成一个默认的 package.json
文件。
接下来,我们需要安装一些必要的依赖项。
在这一步,我们安装了 TypeScript、Node.js 的类型定义以及 tsx。tsx 是一个用于在 Node.js 中运行 TypeScript 的工具。
我们还安装了 graphql 和 graphql-yoga,来帮助我们运行 GraphQL 服务。
现在,使用下面命令创建一个新的 TypeScript 配置文件:
首先,我们在 package.json
中添加一个 dev
脚本,用于启动我们的应用程序:
然后,我们创建一个 src/index.ts
文件,并添加以下代码:
在上面的代码中:我们使用 resolver
和 query
来定义我们的 GraphQL Resolver,通过 weave
函数我们将 CatResolver
编织成 GraphQL Schema ,并使用 graphql-yoga
来启动我们的 GraphQL 服务。
现在,你可以运行以下命令来启动你的应用程序:
你应该会看到类似以下的输出:
你可以打开浏览器并访问 http://localhost:4000/graphql,你将看到一个 GraphQL 演练场,你可以在这里测试你的 GraphQL 查询:
例如,当我们输入:
你应该会看到以下输出:
现在,你已经成功地启动了你的 GraphQL 服务,让我们尝试构造稍微复杂的功能。
接下来,我们定义一个 Cat
类型,它有一个 name
字段和一个 birthDate
字段。
在上面的代码中,我们使用 v.object
来定义 Cat
类型:
它有一个 __typename
字段,它的值是 "Cat"
,当编织 GraphQL Schema 时,此值将作为此对象的名称,我们还将 __typename
设置为 nullish
,如此一来就不必在运行时为每个 Cat
的实例对象携带 __typename
属性;
还有一个 name
字段和一个 birthDate
字段,它们的类型都为字符串。
最后,我们使用 v.InferOutput
轻易地获取了 Cat
的输出类型,并将其命名为 ICat
。
GQLoom
将把我们刚刚定义的 Cat
类型编织成 GraphQL Schema:
为了管理我们的数据,我们简单地使用一个 Map 对象来存储 Cat
实例:
在本篇教程中,为了代码的简洁性,我们直接使用 JavaScript 的 Map
对象来存储数据。这将把数据存储在内存中,当服务器重启时,数据将丢失。
在实际应用中,你可能需要使用更可靠的数据持久化存储解决方案,例如数据库。
query
操作是 GraphQL Schema 的入口,它允许客户端查询数据。
现在,让我们回到最开始的 CatResolver
,并为其添加一个名为 cats
的 query
操作,该操作返回所有 Cat
实例:
在上面的代码中,我们使用 resolver
函数来定义 CatResolver
,并为其添加一个名为 cats
的 query
操作,该操作返回所有 Cat
实例。
query
函数接受两个参数:
cats
的输出类型,你可以直接将 valibot
schema 传入,在这里我们传入的是 v.array(Cat)
;cats
的具体解析逻辑,在这里我们使用 Array.from
函数将 catMap
转换为一个数组,并将其作为 cats
的返回值。另外,我们还将 CatResolver
和 HelloResolver
使用 weave
函数编织在一起,以创建最终的 GraphQL Schema。
让我们在演练场尝试访问 cats
操作:
你应该会看到以下输出:
接下来,我们定义一个名为 cat
的 query
操作,该操作接受一个 name
参数,并返回与该 name
对应的 Cat
实例:
在上面的代码中,我们为 CatResolver
其添加一个名为 cat
的 query
操作。
与 cats
类似,构建 cat
使用的 query
函数的第一个参数为 Cat
的 nullish
类型,表示 cat
操作的返回值可以为 null
或 Cat
类型。
在第二个参数中,我们依旧传入了一个解析函数,但这次我们传入了一个额外的 input
参数,该参数定义了 cat
操作的输入类型。
input
参数是一个对象,其中包含一个名为 name
的属性,该属性的类型为 string
。当 cat
操作被访问时,GQLoom
会在内部调用 valibot
的 parse
函数以确保 name
参数的值符合 string
类型。
在解析函数中,我们从 resolve
函数的第一个参数中获取 name
参数的值,TypeScript 会把 name
参数的类型推断为 string
,然后我们使用 catMap.get
方法获取与 name
对应的 Cat
实例,并将其作为 cat
操作的返回值。
让我们在演练场尝试访问 cat
操作:
你应该会看到以下输出:
mutation
操作用于修改数据,例如创建、更新或删除数据。
现在,让我们为 CatResolver
添加一个名为 createCat
的 mutation
操作,该操作接受一个 name
参数,并返回一个 Cat
实例。
在上面的代码中,我们为 CatResolver
添加了一个名为 createCat
的 mutation
操作。
mutation
函数的输入与 query
函数一致。
在这里,createCat
操作的返回类型为 Cat
,同时接受两个参数 name
和 birthDate
作为输入,它们的类型均为 string
。
在解析函数中,我们可以轻松从第一个参数中获取 name
和 birthDate
参数的值,TypeScript 将会为我们推导其类型,然后我们创建一个新的 Cat
实例,并将其添加到 catMap
中,最后返回该 Cat
实例。
让我们在演练场尝试创建新的 Cat
实例:
你应该会看到类似下面的结果:
让我们使用 cats
查询来获取所有 Cat
实例:
你应该会看到类似下面的结果:
现在,让我们尝试为 Cat
类型定义一个 age
字段。
age
字段并不保存在 Cat
实例中,而是在每次查询时计算。
在上面的代码中,我们为 CatResolver
添加了一个名为 age
的 field
。
注意,我们使用 resolver.of
函数替代了 resolver
。
resolver.of
函数的第一个参数为一个对象 Schema,在此处为 Cat
,它将作为 CatResolver
的 source
类型;
在第二个参数中,我们仍旧传入 query
、mutation
和 field
来定义 CatResolver
。
在名为 age
的 field
中,我们使用 v.pipe(v.number(), v.integer())
来定义 age
的类型,GQLoom
将把 age
字段编织为 GraphQL Int
类型。注意,GQLoom
默认不会对解析函数的输出执行 parse
步骤,这是因为在解析函数内部产生的结果通常可控且符合 TypeScript 推导的类型。
在解析函数中,我们轻松地从第一个参数中获取 cat
实例的值,TypeScript 将会为我们推导其类型,然后我们将 cat
实例的 birthDate
字段转换为 Date
实例,并计算当前年份与 birthDate
的年份之差,最后返回该差值即为猫咪的年龄。
让我们在演练场尝试访问 cat
操作:
你应该会看到以下输出:
我们可以在 field
中添加一个 input
对象,它将作为该 field
的输入参数。
在上面的代码中,我们为 age
字段添加了一个 input
对象,它包含一个名为 year
的字段,该字段为 Int
类型,若未提供 year
输入,则使用当前年份作为默认值。
在 field
解析函数中,我们可以从第二个参数中轻易地获取 year
的值。
让我们在演练场尝试访问 cat
操作:
你应该会看到以下输出:
非常好,我们编写了一个简单的 GraphQL APP,它包含一个 CatResolver
。
在刚刚的例子中,我们学习了:
resolver
中定义 query
、mutation
的方法;valibot
、zod
或 graphql.js
来定义对象和字段;query
、mutation
、field
中定义解析函数和输入参数;weave
函数将 CatResolver
和 HelloResolver
编织为 GraphQL Schema,并使用 graphql-yoga
启动我们的 GraphQL APP。Valibot
构建更复杂的 GraphQL 对象以及 Union、Interface 和 Enum 等高级类型。Zod
构建更复杂的 GraphQL 对象以及 Union、Interface 和 Enum 等高级类型。