Skip to content

Commit a2c76c6

Browse files
committed
Easier context binding
1 parent 0a04a1c commit a2c76c6

File tree

7 files changed

+493
-44
lines changed

7 files changed

+493
-44
lines changed

packages/schema/README.md

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,49 @@ constructing GraphQL Schemas while avoiding type-generation, [declaration mergin
66
and [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html).
77

88
```ts
9-
import { g } from "@graphql-ts/schema";
9+
import { initG } from "@graphql-ts/schema";
1010
import { GraphQLSchema, graphql } from "graphql";
1111

12+
type Context = {
13+
loadPerson: (id: string) => Person | undefined;
14+
loadFriends: (id: string) => Person[];
15+
};
16+
const g = initG<Context>();
17+
type g<
18+
Key extends initG.Key,
19+
Arg extends initG.Arg[Key] = initG.ArgDefaults[Key],
20+
OtherArg extends initG.OtherArg[Key] = initG.OtherArgDefaults<Arg>[Key],
21+
> = initG<Context, Key, Arg, OtherArg>;
22+
23+
type Person = {
24+
id: string;
25+
name: string;
26+
};
27+
28+
const Person: g<"object", Person> = g.object<Person>()({
29+
name: "Person",
30+
fields: () => ({
31+
id: g.field({ type: g.nonNull(g.ID) }),
32+
name: g.field({ type: g.nonNull(g.String) }),
33+
friends: g.field({
34+
type: g.list(g.nonNull(Person)),
35+
resolve(source, _, context) {
36+
return context.loadFriends(source.id);
37+
},
38+
}),
39+
}),
40+
});
41+
1242
const Query = g.object()({
1343
name: "Query",
1444
fields: {
15-
hello: g.field({
16-
type: g.String,
17-
resolve() {
18-
return "Hello!";
45+
person: g.field({
46+
type: Person,
47+
args: {
48+
id: g.arg({ type: g.nonNull(g.ID) }),
49+
},
50+
resolve(_, args, context) {
51+
return context.loadPerson(args.id);
1952
},
2053
}),
2154
},
@@ -25,14 +58,40 @@ const schema = new GraphQLSchema({
2558
query: Query,
2659
});
2760

28-
graphql({
29-
source: `
30-
query {
31-
hello
32-
}
33-
`,
34-
schema,
35-
}).then((result) => {
36-
console.log(result);
37-
});
61+
{
62+
const people = new Map<string, Person>([
63+
["1", { id: "1", name: "Alice" }],
64+
["2", { id: "2", name: "Bob" }],
65+
]);
66+
const friends = new Map<string, string[]>([
67+
["1", ["2"]],
68+
["2", ["1"]],
69+
]);
70+
const contextValue: Context = {
71+
loadPerson: (id) => people.get(id),
72+
loadFriends: (id) => {
73+
return (friends.get(id) ?? [])
74+
.map((id) => people.get(id))
75+
.filter((person) => person !== undefined) as Person[];
76+
},
77+
};
78+
graphql({
79+
source: `
80+
query {
81+
person(id: "1") {
82+
id
83+
name
84+
friends {
85+
id
86+
name
87+
}
88+
}
89+
}
90+
`,
91+
schema,
92+
contextValue,
93+
}).then((result) => {
94+
console.log(result);
95+
});
96+
}
3897
```

packages/schema/src/api-without-context/index.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,26 @@ import type {
3232
GType,
3333
} from "../types";
3434

35-
/** @deprecated Use {@link GEnumType} instead */
35+
/** @deprecated Use {@link GEnumType} or `g<'enum', ...>` instead */
3636
export type EnumType<Values extends { [key: string]: unknown }> =
3737
GEnumType<Values>;
38-
/** @deprecated Use {@link GArg} instead */
38+
/** @deprecated Use {@link GArg} or `g<'arg', ...>` instead */
3939
export type Arg<
4040
Type extends GInputType,
4141
HasDefaultValue extends boolean = boolean,
4242
> = GArg<Type, HasDefaultValue>;
43-
/** @deprecated Use {@link GList} instead */
43+
/** @deprecated Use {@link GList} or `g<'list', ...>` instead */
4444
export type ListType<Of extends GType<any>> = GList<Of>;
45-
/** @deprecated Use {@link GNonNull} instead */
45+
/** @deprecated Use {@link GNonNull} or `g<'nonNull', ...>` instead */
4646
export type NonNullType<Of extends GNullableType<any>> = GNonNull<Of>;
4747

48-
/** @deprecated Use {@link GScalarType} instead */
48+
/** @deprecated Use {@link GScalarType} or `g<'scalar', ...>` instead */
4949
export type ScalarType<Internal, External = Internal> = GScalarType<
5050
Internal,
5151
External
5252
>;
5353

54-
/** @deprecated Use {@link GInputObjectType} instead */
54+
/** @deprecated Use {@link GInputObjectType} or `g<'inputObject', ...>` instead */
5555
export type InputObjectType<
5656
Fields extends {
5757
[key: string]: IsOneOf extends true
@@ -60,7 +60,7 @@ export type InputObjectType<
6060
},
6161
IsOneOf extends boolean = false,
6262
> = GInputObjectType<Fields, IsOneOf>;
63-
/** @deprecated Use {@link GNullableInputType} instead */
63+
/** @deprecated Use {@link GNullableInputType} or `g<'nullableInputType'>` instead */
6464
export type NullableInputType = GNullableInputType;
65-
/** @deprecated Use {@link GInputType} instead */
65+
/** @deprecated Use {@link GInputType} or `g<'inputType'>` instead */
6666
export type InputType = GInputType;

packages/schema/src/index.ts

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,50 @@
77
* [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html).
88
*
99
* ```ts
10-
* import { g } from "@graphql-ts/schema";
10+
* import { initG } from "@graphql-ts/schema";
1111
* import { GraphQLSchema, graphql } from "graphql";
1212
*
13+
* type Context = {
14+
* loadPerson: (id: string) => Person | undefined;
15+
* loadFriends: (id: string) => Person[];
16+
* };
17+
* const g = initG<Context>();
18+
* type g<
19+
* Key extends initG.Key,
20+
* Arg extends initG.Arg[Key] = initG.ArgDefaults[Key],
21+
* OtherArg extends
22+
* initG.OtherArg[Key] = initG.OtherArgDefaults<Arg>[Key],
23+
* > = initG<Context, Key, Arg, OtherArg>;
24+
*
25+
* type Person = {
26+
* id: string;
27+
* name: string;
28+
* };
29+
*
30+
* const Person: g<"object", Person> = g.object<Person>()({
31+
* name: "Person",
32+
* fields: () => ({
33+
* id: g.field({ type: g.nonNull(g.ID) }),
34+
* name: g.field({ type: g.nonNull(g.String) }),
35+
* friends: g.field({
36+
* type: g.list(g.nonNull(Person)),
37+
* resolve(source, _, context) {
38+
* return context.loadFriends(source.id);
39+
* },
40+
* }),
41+
* }),
42+
* });
43+
*
1344
* const Query = g.object()({
1445
* name: "Query",
1546
* fields: {
16-
* hello: g.field({
17-
* type: g.String,
18-
* resolve() {
19-
* return "Hello!";
47+
* person: g.field({
48+
* type: Person,
49+
* args: {
50+
* id: g.arg({ type: g.nonNull(g.ID) }),
51+
* },
52+
* resolve(_, args, context) {
53+
* return context.loadPerson(args.id);
2054
* },
2155
* }),
2256
* },
@@ -26,21 +60,52 @@
2660
* query: Query,
2761
* });
2862
*
29-
* graphql({
30-
* source: `
31-
* query {
32-
* hello
33-
* }
34-
* `,
35-
* schema,
36-
* }).then((result) => {
37-
* console.log(result);
38-
* });
63+
* {
64+
* const people = new Map<string, Person>([
65+
* ["1", { id: "1", name: "Alice" }],
66+
* ["2", { id: "2", name: "Bob" }],
67+
* ]);
68+
* const friends = new Map<string, string[]>([
69+
* ["1", ["2"]],
70+
* ["2", ["1"]],
71+
* ]);
72+
* const contextValue: Context = {
73+
* loadPerson: (id) => people.get(id),
74+
* loadFriends: (id) => {
75+
* return (friends.get(id) ?? [])
76+
* .map((id) => people.get(id))
77+
* .filter((person) => person !== undefined) as Person[];
78+
* },
79+
* };
80+
* graphql({
81+
* source: `
82+
* query {
83+
* person(id: "1") {
84+
* id
85+
* name
86+
* friends {
87+
* id
88+
* name
89+
* }
90+
* }
91+
* }
92+
* `,
93+
* schema,
94+
* contextValue,
95+
* }).then((result) => {
96+
* console.log(result);
97+
* });
98+
* }
3999
* ```
40100
*
41101
* @module
42102
*/
43103
export * as g from "./schema-api";
104+
export type g<
105+
Key extends initG.Key,
106+
FirstArg extends initG.Arg[Key] = initG.ArgDefaults[Key],
107+
SecondArg extends initG.OtherArg[Key] = initG.OtherArgDefaults<FirstArg>[Key],
108+
> = initG<unknown, Key, FirstArg, SecondArg>;
44109
export { initG, type GWithContext } from "./output";
45110

46111
export {
@@ -78,6 +143,7 @@ export type {
78143
ScalarType,
79144
} from "./api-without-context";
80145

146+
import { initG } from "./output";
81147
import type {
82148
GArg,
83149
GField,

0 commit comments

Comments
 (0)