GQLoom

Valibot

Valibot 是一个通用的数据验证库,考虑了捆绑包大小、类型安全和开发体验。

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

安装

npm i @gqloom/core valibot @gqloom/valibot

定义简单标量

在 GQLoom 中,可以直接 Valibot Schema 作为丝线使用:

import * as  from "valibot"

const  = .() // GraphQLString

const  = .() // GraphQLBoolean

const  = .() // GraphQLFloat

const  = .(.(), .()) // GraphQLInt

编织 | Weave

为了让 GQLoom 能正确地将 Valibot Schema 编织到 GraphQL Schema,我们在使用 weave 函数时,需要添加来自 @gqloom/valibotValibotWeaver

import { , ,  } from "@gqloom/core"
import {  } from "@gqloom/valibot"
import * as  from "valibot"

export const  = ({
  : (.(), () => "Hello, World!"),
})

export const  = (, )

定义对象

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

import * as  from "valibot"

export const  = .({
  : .(.("Cat")),
  : .(),
  : .(.(), .()),
  : .(.()),
})

名称和更多元数据

为对象定义名称

GQLoom 中,我们有多种方法来为对象定义名称。

使用 __typename 字面量

import * as  from "valibot"

export const  = .({
  : .(.("Cat")),
  : .(),
  : .(.(), .()),
  : .(.()),
})

在上面的代码中,我们使用 __typename 字面量来为对象定义名称。我们还将 __typename 字面量设置为 nullish,这意味着 __typename 字段是可选的,如果存在,则必须为 "Cat"。

import * as  from "valibot"

export const  = .({
  : .("Cat"),
  : .(),
  : .(.(), .()),
  : .(.()),
})

在上面的代码中,我们仍旧使用 __typename 字面量来为对象定义名称,但这次我们将 __typename 字面量设置为 "Cat",这意味着 __typename 字段是必须的,且必须为 "Cat"。当使用 GraphQL interfaceunion 时,必填的 __typename 将非常有用。

使用 collectNames

import {  } from "@gqloom/core"
import * as  from "valibot"

export const  = .({
  : .(),
  : .(.(), .()),
  : .(.()),
})

({  }) // 为 Cat 收集名称,在编织后将呈现在 GraphQL Schema 中

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

import {  } from "@gqloom/core"
import * as  from "valibot"

export const {  } = ({
  : .({
    : .(),
    : .(.(), .()),
    : .(.()),
  }),
})

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

使用 asObjectType

import {  } from "@gqloom/valibot"
import * as  from "valibot"

export const  = .(
  .({
    : .(),
    : .(.(), .()),
    : .(.()),
  }),
  ({ : "Cat" })
)

在上面的代码中,我们使用 asObjectType 函数创建一个元数据并将其传入 Valibot 管道中来为对象定义名称。asObjectType 函数接受完整的 GraphQL 对象类型定义,并返回一个元数据。

添加更多元数据

通过 asObjectType 函数,我们可以为对象添加更多元数据,例如 descriptiondeprecationReasonextensions 等。

import {  } from "@gqloom/valibot"
import * as  from "valibot"

export const  = .(
  .({
    : .(),
    : .(.(), .()),
    : .(.()),
  }),
  ({
    : "Cat",
    : "A cute cat",
  })
)

在上面的代码中,我们为 Cat 对象添加了一个 description 元数据,该元数据将在 GraphQL Schema 中呈现:

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

我们还可以使用 asField 函数为字段添加元数据,例如 descriptiontype 等。

import { ,  } from "@gqloom/valibot"
import {  } from "graphql"
import * as  from "valibot"

export const  = .(
  .({
    : .(),
    : .(
      .(),
      ({ : , : "How old is the cat" })
    ),
    : .(.()),
  }),
  ({
    : "Cat",
    : "A cute cat",
  })
)

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

GraphQL Schema
"""A cute cat"""
type Cat {
  name: String!

  """How old is the cat"""
  age: Int
  loveFish: Boolean
}

声明接口

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

import {  } from "@gqloom/valibot"
import * as  from "valibot"

const  = .({
  : .(.("Fruit")),
  : .(),
  : .(),
  : .(),
})

const  = .(
  .({
    : .(.("Orange")),
    : .(),
    : .(),
    : .(),
  }),
  ({ : [] })
)

在上面的代码中,我们使用 asObjectType 函数创建了一个接口 Fruit,并使用 interfaces 选项将 Orange 对象声明为 Fruit 接口的实现。

省略字段

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

import {  } from "@gqloom/valibot"
import * as  from "valibot"

const  = .({
  : .(.("Dog")),
  : .(.()),
  : .(.(.()), ({ : null })),
})

将得到如下 GraphQL Schema:

GraphQL Schema
type Dog {
  name: String
}

定义联合类型

在使用 Valibot 时,我们可以使用 variantunion 定义联合类型。

使用 variant

我们推荐使用 variant 来定义联合类型:

import {  } from "@gqloom/valibot"
import * as  from "valibot"

const  = .({
  : .("Cat"),
  : .(),
  : .(.(), .()),
  : .(.()),
})

const  = .({
  : .("Dog"),
  : .(),
  : .(.(), .()),
  : .(.()),
})

const  = .(
  .("__typename", [, ]),
  ({ : "Animal" })
)

在上面的代码中,我们使用 variant 函数创建了一个联合类型。对于 Animal 来说,它通过 __typename 字段来区分具体的类型。

使用 union

我们还可以使用 union 来定义联合类型:

import {  } from "@gqloom/core"
import {  } from "@gqloom/valibot"
import * as  from "valibot"

const  = .({
  : .(),
  : .(.(), .()),
  : .(.()),
})

const  = .({
  : .(),
  : .(.(), .()),
  : .(.()),
})

const  = .(
  .([, ]),
  ({
    : () => (.loveFish ? "Cat" : "Dog"),
  })
)

({ , ,  })

在上面的代码中,我们使用 union 函数创建了一个联合类型。对于 Animal 来说,它通过 resolveType 函数来区分具体的类型。 在这里,如果一个动物它喜欢鱼,那么它就是一只猫,否则就是一只狗。

定义枚举类型

我们可以使用 v.picklistv.enum_ 定义枚举类型。

使用 picklist

通常,我们更推荐使用 v.picklist 来定义枚举类型:

import {  } from "@gqloom/valibot"
import * as  from "valibot"

export const  = .(
  .(["apple", "banana", "orange"]),
  ({
    : "Fruit",
    : {
      : { : "red" },
      : { : "yellow" },
      : { : "orange" },
    },
  })
)

export type  = .<typeof >

使用 enum_

我们也可以使用 v.enum_ 来定义枚举类型:

import {  } from "@gqloom/valibot"
import * as  from "valibot"

export enum  {
   = "apple",
   = "banana",
   = "orange",
}

export const  = .(
  .(),
  ({
    : "Fruit",
    : {
      : { : "red" },
      [.]: { : "yellow" },
      [.]: { : "orange" },
    },
  })
)

自定义类型映射

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

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

import {
  ,
  ,
  ,
} from "graphql-scalars"
import {  } from "@gqloom/valibot"

export const  = .({
  : () => {
    switch (.) {
      case "date":
        return 
      case "any":
        return 
      case "record":
        return 
    }
  },
})

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

import {  } from "@gqloom/core"

export const  = (, )

默认类型映射

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

Valibot 类型GraphQL 类型
v.array()GraphQLList
v.bigint()GraphQLInt
v.date()GraphQLString
v.enum_()GraphQLEnumType
v.picklist()GraphQLEnumType
v.literal(false)GraphQLBoolean
v.literal(0)GraphQLFloat
v.literal("")GraphQLString
v.looseObject()GraphQLObjectType
v.object()GraphQLObjectType
v.objectWithRest()GraphQLObjectType
v.strict_object()GraphQLObjectType
v.nonNullable()GraphQLNonNull
v.nonNullish()GraphQLNonNull
v.nonOptional()GraphQLNonNull
v.number()GraphQLFloat
v.pipe(v.number(), v.integer())GraphQLInt
v.string()GraphQLString
v.pipe(v.string(), v.cuid2())GraphQLID
v.pipe(v.string(), v.ulid())GraphQLID
v.pipe(v.string(), v.uuid())GraphQLID
v.union()GraphQLUnionType
v.variant()GraphQLUnionType