@gqloom/core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # GQLoom
2
+
3
+ English | [简体中文](./README.zh-CN.md)
4
+
5
+ GQLoom is a GraphQL weaver for TypeScript/JavaScript, using Zod, Yup, or Valibot to easily weave GraphQL schemas, providing the best development experience with complete type inference.
6
+
7
+ GQLoom is inspired by [tRPC](https://trpc.io/), [TypeGraphQL](https://typegraphql.com/), and [Pothos](https://pothos-graphql.dev/).
8
+
9
+ ## Features
10
+
11
+ - 📦 Use popular pattern libraries (Zod, Yup, Valibot) to build and validate GraphQL schemas.
12
+ - 🔒 Complete type safety, discover potential issues during compilation.
13
+ - 🧩 Classic middleware system: authentication, caching, logging, etc.
14
+ - 🪄 Accessible Context and DataLoader everywhere.
15
+ - 🔮 No code generation and experimental decorator features.
16
+
17
+ ## Hello, World!
18
+
19
+ ```ts
20
+ import { weave } from "@gqloom/core"
21
+ import { resolver, query } from "@gqloom/valibot"
22
+ import * as v from "valibot"
23
+
24
+ const HelloResolver = resolver({
25
+ hello: query(v.string(), () => "world"),
26
+ })
27
+
28
+ export const schema = weave(HelloResolver)
29
+ ```
@@ -0,0 +1,267 @@
1
+ # [GQLoom](../../README.zh-CN.md)
2
+
3
+ [English](./README.md) | 简体中文
4
+
5
+ [GQLoom](../../README.zh-CN.md) 是一个用于 TypeScript/JavaScript 的 GraphQL 编织器,使用 Zod、Yup 或者 Valibot 来愉快地编织 GraphQL Schema, 支持完善的类型推断以提供最好的开发体验。
6
+
7
+ ## 你好,世界!
8
+
9
+ ```ts
10
+ import { weave, loom, silk } from "@gqloom/core"
11
+ import { GraphQLString } from "graphql"
12
+
13
+ const HelloResolver = loom.resolver({
14
+ hello: loom.query(silk(GraphQLString), () => "world"),
15
+ })
16
+
17
+ export const schema = weave(HelloResolver)
18
+ ```
19
+
20
+ # GQLoom Core
21
+
22
+ GQLoom Core 是 GQLoom 的核心库,提供了 GQLoom 的基础功能。
23
+
24
+ ## 概念
25
+
26
+ GQLoom 意为 GraphQL 的织布机(Loom),其核心理念是从各式Schema 编织出 GraphQL Schema。
27
+
28
+ ### 丝线|Silk
29
+
30
+ 丝线是 GQLoom 的基础单位,它同时反应 GraphQL 类型和 TypeScript 类型。
31
+ 我们可以通过 `silk` 函数创建丝线:
32
+
33
+ ##### 简单的标量丝线
34
+
35
+ ```ts
36
+ import { silk } from "@gqloom/core"
37
+ import { GraphQLString, GraphQLInt } from "graphql"
38
+
39
+ const StringSilk = silk(GraphQLString)
40
+ const IntSilk = silk(GraphQLInt)
41
+ ```
42
+
43
+ ##### 携带类型的对象丝线
44
+
45
+ ```ts
46
+ import { silk } from "@gqloom/core"
47
+ import {
48
+ GraphQLObjectType,
49
+ GraphQLNonNull,
50
+ GraphQLString,
51
+ GraphQLInt,
52
+ } from "graphql"
53
+
54
+ interface ICat {
55
+ name: string
56
+ age: number
57
+ }
58
+
59
+ // 这里我们使用 `silk`的泛型参数将 Cat 的类型标记为 ICat
60
+ export const Cat = silk<ICat>(
61
+ new GraphQLObjectType({
62
+ name: "Cat",
63
+ fields: {
64
+ name: { type: new GraphQLNonNull(GraphQLString) },
65
+ age: { type: new GraphQLNonNull(GraphQLInt) },
66
+ },
67
+ })
68
+ )
69
+ ```
70
+
71
+ 直接使用 [graphql.js](https://graphql.org/graphql-js/constructing-types/) 构造 GraphQL 类型可能导致冗长的代码,并且需要手动为丝线添加类型声明。
72
+ 使用 Zod、Valibot 可以更轻松地创建丝线。
73
+
74
+ ##### 使用 Zod 创建丝线
75
+
76
+ ```ts
77
+ import { zodSilk } from "@gqloom/zod"
78
+ import { z } from "zod"
79
+
80
+ const Cat = zodSilk(
81
+ z.object({
82
+ __typename: z.literal("Cat").nullish(), // 定义 GraphQL Object 的名称
83
+ name: z.string(),
84
+ age: z.number(),
85
+ })
86
+ )
87
+ ```
88
+
89
+ ##### 使用 Valibot 创建丝线
90
+
91
+ ```ts
92
+ import { valibotSilk } from "@gqloom/valibot"
93
+ import * as v from "valibot"
94
+
95
+ const Cat = valibotSilk(
96
+ v.object({
97
+ __typename: v.nullish(v.literal("Cat")), // 定义 GraphQL Object 的名称
98
+ name: v.string(),
99
+ age: v.number(),
100
+ })
101
+ )
102
+ ```
103
+
104
+ ### 解析器|Resolver
105
+
106
+ 解析器是放置 GraphQL 操作(Query、Mutation、Subscription)的地方,GQLoom 会将各个解析器汇总编织成 GraphQL Schema。
107
+
108
+ ```ts
109
+ import { loom, silk } from "@gqloom/core"
110
+ import { GraphQLString } from "graphql"
111
+
112
+ const { resolver, query, mutation } = loom
113
+
114
+ const HelloResolver = resolver({
115
+ hello: query(silk(GraphQLString), () => "world"),
116
+ bye: mutation(silk(GraphQLString), {
117
+ description: "say bye",
118
+ resolve: () => "see you later",
119
+ }),
120
+ })
121
+ ```
122
+
123
+ 解析器内可以存放多个操作。操作通过 `query`、`mutation`或`subscription` 函数创建,它们接受两个参数:
124
+
125
+ - 1. 丝线:定义了操作返回值的类型;
126
+ - 2. 解析函数以及更多配置:定义了操作的具体逻辑、输入类型及其他配置。
127
+
128
+ #### 带输入的解析操作
129
+
130
+ 为 GraphQL 操作添加输入类型,只需要在解析函数中添加一个 `input` 参数即可。
131
+
132
+ ```ts
133
+ import { loom, silk } from "@gqloom/core"
134
+ import { GraphQLString } from "graphql"
135
+
136
+ const { resolver, query } = loom
137
+
138
+ const GreetingResolver = resolver({
139
+ greet: query(silk(GraphQLString), {
140
+ input: { name: silk(GraphQLString) },
141
+ resolve: ({ name }) => `Hello, ${name}!`,
142
+ }),
143
+ })
144
+ ```
145
+
146
+ 在以上代码中,我们定义了一个 `greet` 操作,它接受一个 `name` 作为输入,然后我们可以在 `resolve` 函数中轻松获取到 `name` 的值。
147
+
148
+ #### 为对象添加更多字段
149
+
150
+ 我们可以使用 `field` 函数在解析器内为对象添加额外的字段。
151
+
152
+ ```ts
153
+ import { loom, silk } from "@gqloom/core"
154
+ import { GraphQLInt } from "graphql"
155
+ import { Cat } from "./schemas"
156
+
157
+ const CatResolver = resolver.of(Cat, {
158
+ cat: query(Cat, () => ({
159
+ name: "Tom",
160
+ birthday: "2020-01-01",
161
+ })),
162
+
163
+ // 我们可以在解析函数的第一个参数获取到 `Cat` 实例的值
164
+ age: field(silk(GraphQLInt), (cat) => {
165
+ return new Date().getFullYear() - new Date(cat.birthday).getFullYear()
166
+ }),
167
+
168
+ ageAt: field(silk(GraphQLInt), {
169
+ input: { year: silk(GraphQLInt) },
170
+ // 当字段包含输入时,我们可以在第二个参数中获取到输入的值
171
+ resolve: (cat, { year }) => {
172
+ return year - new Date(cat.birthday).getFullYear()
173
+ },
174
+ }),
175
+
176
+ // 在对象之间建立关联
177
+ friend: field(Cat, () => ({
178
+ name: "Jerry",
179
+ birthday: "2020-01-01",
180
+ })),
181
+ })
182
+ ```
183
+
184
+ ### 编织器|Weaver
185
+
186
+ 使用 `weave` 函数将解析器编织成 GraphQL Schema。
187
+ `weave` 能够接受解析器、丝线、中间件、各式配置作为输入,将在之后介绍。
188
+
189
+ ```ts
190
+ import { weave } from "@gqloom/core"
191
+ import { HelloResolver } from "./resolvers"
192
+
193
+ export const schema = weave(HelloResolver, GreetingResolver)
194
+ ```
195
+
196
+ ### 中间件|Middleware
197
+
198
+ GQLoom 提供了洋葱式中间件机制,可以在解析器执行之前或之后执行逻辑。
199
+
200
+ ##### 声明一个简单中间件
201
+
202
+ ```ts
203
+ import { Middleware } from "@gqloom/core"
204
+
205
+ const simpleMiddleware: Middleware = async (next) => {
206
+ console.log("Before resolve")
207
+ const result = await next()
208
+ console.log("After resolve")
209
+ return result
210
+ }
211
+ ```
212
+
213
+ ### 上下文|Context
214
+
215
+ 使用 `useContext` 函数随处获取上下文。
216
+
217
+ ##### 在解析器中
218
+
219
+ ```ts
220
+ import { loom, silk, useContext } from "@gqloom/core"
221
+ import { GraphQLString } from "graphql"
222
+
223
+ const { query, mutation } = loom
224
+
225
+ const HelloResolver = resolver({
226
+ hello: query(silk(GraphQLString), () => {
227
+ const name = useContext().info.name
228
+ return `Hello, ${name}!`
229
+ }),
230
+ })
231
+ ```
232
+
233
+ ##### 在中间件中
234
+
235
+ ```ts
236
+ import { Middleware, useContext } from "@gqloom/core"
237
+
238
+ const simpleMiddleware: Middleware = async (next) => {
239
+ const context = useContext()
240
+ console.log(`Hello, ${context.info.name}!`)
241
+ return next()
242
+ }
243
+ ```
244
+
245
+ Context 的内容取决于你选择的适配器。
246
+
247
+ #### 上下文记忆|Context Memoization
248
+
249
+ 我们有时需要在上下文中设置一些内容,并且在同一个上下文的中间件或解析器中获取同样的值,例如当前访问用户、DataLoader。
250
+ GQLoom 提供了上下文记忆来便捷地实现这一点。
251
+
252
+ ```ts
253
+ import { createMemoization, useContext } from "@gqloom/core"
254
+
255
+ // 创建一个上下文记忆
256
+ const useVisitor = createMemoization(async () => {
257
+ const context = useContext()
258
+ return await fetchVisitor(context.info.visitorId)
259
+ })
260
+
261
+ // 在中间件中使用上下文记忆
262
+ const AllowAdmin: Middleware = async (next) => {
263
+ const visitor = await useVisitor()
264
+ if (visitor.role !== "admin") throw new Error("Forbidden")
265
+ return next()
266
+ }
267
+ ```