Yup

Yup 是一个用于运行时值解析和验证的模式构建器。 您可以定义模式、转换值以匹配、断言现有值的形状,或两者兼而有之。 Yup 模式具有极强的表现力,可对复杂、相互依赖的验证或值转换进行建模。

@gqloom/yup 提供了 GQLoom 与 Yup 的集成,以便将 Yup Schema 编织成 GraphQL Schema。

安装

npm
yarn
pnpm
bun
npm install @gqloom/core yup @gqloom/yup

另外,我们还需要在项目中为 Yup 声明来自 GQLoom 的元数据:

import 'yup' import { type GQLoomMetadata } from "@gqloom/yup" declare module "yup" { export interface CustomSchemaMetadata extends GQLoomMetadata {} }

定义简单标量

在 GQLoom 中,可以使用 yupSilk 将 Yup Schema 作为丝线使用:

import { number, string, boolean } from "yup" import { yupSilk } from "../src" const StringScalar = yupSilk(string()) const BooleanScalar = yupSilk(boolean()) const FloadtScalar = yupSilk(number()) const IntScalar = yupSilk(number().integer())

解析器 | Resolver

为了将 Yup Schema 作为丝线使用,我们需要为其包裹 yupSilk,在开发中大量的包裹可能会显得有些繁琐,因此 @gqloom/yup 提供了重新导出的解析器和操作构造函数来简化这个过程。 从 @gqloom/yup 引入的 resolverquerymutationfield 将在内部自动包裹 yupSilk,这样在大部分情况下,我们可以直接使用 Yup Schema。

import { string } from "yup" import { resolver, query } from "../src" export const HelloResolver = resolver({ hello: query(string(), () => "Hello, World!"), })

定义对象

我们可以使用 Yup 定义对象,并将其作为丝线使用:

import { string, boolean, object, number } from "yup" export const Cat = object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }).label("Cat")

名称和更多元数据

为对象定义名称

使用label()

import { string, boolean, object, number } from "yup" export const Cat = object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }).label("Cat")

在上面的代码中,我们使用 label 为对象定义了名称,这样在生成的 GraphQL Schema 中,该对象将具有名称 Cat

使用collectNames

import { string, boolean, object, number } from "yup" import { collectNames } from "../src" export const Cat = object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }) collectNames({ Cat })

在上面的代码中,我们使用 collectNames 函数来为对象定义名称。collectNames 函数接受一个对象,该对象的键是对象的名称,值是对象本身。

import { string, boolean, object, number } from "yup" import { collectNames } from "../src" export const { Cat } = collectNames({ Cat: object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }), })

在上面的代码中,我们使用 collectNames 函数来为对象定义名称,并将返回的对象解构为 Cat 并导出。

使用asObjectType 元数据

import { string, boolean, object, number } from "yup" export const Cat = object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }).meta({ asObjectType: { name: "Cat" } })

在上面的代码中,我们在 Yup Schema 中使用 meta 函数来为对象定义名称。 在这里,我们定义了名称为 asObjectType 元数据,并将其设置为 { name: "Cat" },这样在生成的 GraphQL Schema 中,该对象将具有名称 Cat

添加更多元数据

我们可以在 Yup Schema 中使用 meta 函数来添加更多元数据,例如 descriptiondeprecationReasonextensions 等。

import { string, boolean, object, number } from "yup" export const Cat = object({ name: string().required(), age: number().integer().required(), loveFish: boolean(), }).meta({ asObjectType: { name: "Cat", description: "A cute cat" } })

在上面的代码中,我们为 Cat 对象添加了 description 元数据,这样在生成的 GraphQL Schema 中,该对象将具有描述 A cute cat

"""A cute cat""" type Cat { name: String! age: Int! loveFish: Boolean }

我们还可以使用元数据中的 asField 属性为字段添加元数据,例如 descriptiontype 等:

import { string, boolean, object, number } from "yup" import { GraphQLInt } from "graphql" export const Cat = object({ name: string().required(), age: number().meta({ asField: { type: () => GraphQLInt, description: "How old is the cat" }, }), loveFish: boolean(), }).meta({ asObjectType: { name: "Cat", description: "A cute cat" } })

在上面的代码中,我们为 age 字段添加了 typedescription 元数据,最终得到如下 GraphQL Schema:

"""A cute cat""" type Cat { name: String! """How old is the cat""" age: Int loveFish: Boolean }

声明接口

我们还可以使用 asObjectType 函数来声明接口,例如:

import { string, object, number } from "yup" const Fruit = object({ name: string().required(), color: string().required(), prize: number() .required() .meta({ description: "How much do you want to win?" }), }) .meta({ description: "Fruit Interface" }) .label("Fruit") const Orange = object({ name: string().required(), color: string().required(), prize: number().required(), }) .meta({ asObjectType: { interfaces: [Fruit] } }) .label("Orange")

在上面的代码中,我们使用 asObjectType 中的 interfaces 属性将 Orange 对象声明为 Fruit 接口的实现。

省略字段

我们还可以使用 asField 属性将 type 设置为 null 来省略字段,例如:

import { string, boolean, object, number } from "yup" export const Cat = object({ name: string().required(), age: number() .integer() .meta({ asField: { description: "How old is the cat" } }), loveFish: boolean(), }).meta({ asObjectType: { name: "Cat", description: "A cute cat" } })

将得到如下 GraphQL Schema:

type Dog { name: String }

定义联合类型

使用来自 @gqloom/yupunion 定义联合类型,例如:

import { object, string, number } from "yup" import { union } from "@gqloom/yup" const Cat = object({ name: string(). required(), color: string().required(), }).label("Cat") const Dog = object({ name: string().required(), height: number().required(), }).label("Dog") const Animal = union([Cat, Dog]).label("Animal")

在上面的代码中,我们使用 union 函数将 CatDog 对象声明为 Animal 联合类型的成员。

定义枚举类型

使用oneof()

我们可以使用 string().oneof() 来定义枚举类型,例如:

import { string } from "yup" const Fruit = string() .oneOf(["apple", "banana", "orange"]) .label("Fruit") .meta({ asEnumType: { description: "Some fruits you might like", valuesConfig: { apple: { description: "Apple is red" }, banana: { description: "Banana is yellow" }, orange: { description: "Orange is orange" }, }, }, })

使用enum

我们还可以使用 enum 来定义枚举类型,例如:

import { mixed } from "yup" enum FruitEnum { apple, banana, orange, } const Fruit = mixed() .oneOf(Object.values(FruitEnum) as FruitEnum[]) .label("Fruit") .meta({ asEnumType: { enum: FruitEnum, description: "Some fruits you might like", valuesConfig: { apple: { description: "Apple is red" }, banana: { description: "Banana is yellow" }, orange: { description: "Orange is orange" }, }, }, })

自定义类型映射

为了适应更多的 Yup 类型,我们可以拓展 GQLoom 为其添加更多的类型映射。

首先我们使用 YupWeaver.config 来定义类型映射的配置。这里我们导入来自 graphql-scalarsGraphQLDateTime,当遇到 date 类型时,我们将其映射到对应的 GraphQL 标量。

import { GraphQLDateTime } from "graphql-scalars" import { YupWeaver } from "@gqloom/yup" export const yupWeaverConfig = YupWeaver.config({ presetGraphQLType: (description) => { switch (description.type) { case "date": return GraphQLDateTime } }, })

在编织 GraphQL Schema 时传入配置到 weave 函数中:

import { weave } from "@gqloom/yup" export const schema = weave(yupWeaverConfig, HelloResolver)

默认类型映射

下表列出了 GQLoom 中 Yup 类型与 GraphQL 类型之间的默认映射关系:

Yup 类型GraphQL 类型
string()GraphQLString
number()GraphQLFloat
number().integer()GraphQLInt
boolean()GraphQLBoolean
object()GraphQLObjectType
array()GraphQLList
union()GraphQLUnionType
string().oneof(["Value1"])GraphQLEnumType