Skip to content

Commit fe50d58

Browse files
authored
feat: granular output config (#991)
1 parent 40d51c5 commit fe50d58

30 files changed

+1357
-809
lines changed

DOCUMENTATION_NEXT.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,115 @@
11
# GraphQL Request Documentation
22

3-
# Return Mode
3+
# Output
44

5-
GraphQL execution has this general pattern:
5+
The standard GraphQL execution result type in the JavaScript ecosystem (from the `graphql` package) has roughly this type:
66

77
```ts
88
interface GraphQLExecutionResult {
99
data?: object
1010
errors?: GraphQLError[]
11-
extensions?: []
11+
extensions?: unknown[]
1212
}
1313
```
1414

15-
You can change the output of client methods by configuring its return mode. This allows you to tailor the client better to your specific use-case.
15+
Graffle can return this type but also many other types depending on your configuration. For example:
1616

17-
The only client method that is not affected by return mode is `raw` which will _always_ return a standard GraphQL result type.
17+
1. Return the data directly without an envelope.
18+
1. Return all or some categories of errors (return type becomes a union).
19+
1. Return an envelope and place all or some categories of errors into the `errors` field.
20+
1. Throw all or some categories of errors.
1821

19-
Here is a summary table of the modes:
22+
Configuration can be done at the constructor level. Method level will also be supported in the future.
2023

21-
| Mode | Throw Sources (no type safety) | Returns (type safe) |
22-
| ---------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
23-
| `graphql` | Extensions, Fetch | `GraphQLExecutionResult` |
24-
| `graphqlSuccess` | Extensions, Fetch, GraphQLExecutionResult.errors | `GraphQLExecutionResult` with `.errors` always missing. |
25-
| `data` (default) | Extensions, Fetch, GraphQLExecutionResult.errors | `GraphQLExecutionResult.data` |
26-
| `dataSuccess` | Extensions, Fetch, GraphQLExecutionResult.errors, GraphQLExecutionResult.data Schema Errors | `GraphQLExecutionResult.data` without any schema errors |
27-
| `dataAndErrors` | | `GraphQLExecutionResult.data`, errors from: Extensions, Fetch, GraphQLExecutionResult.errors |
24+
```ts
25+
// Constructor Level
26+
27+
const graffle = Graffle.create({
28+
output: {
29+
errors: {
30+
execution: 'throw',
31+
other: 'return',
32+
},
33+
},
34+
})
35+
36+
// Method Level (planned, not implemented yet)
37+
38+
await graffle.query.foo({}, {
39+
output: {
40+
envelope: true,
41+
},
42+
})
43+
```
2844

29-
## `graphql`
45+
## Errors
3046

31-
Return the standard graphql execution output.
47+
There are three categories of errors:
3248

33-
## `graphqlSuccess`
49+
1. `execution` – Anything that went wrong during execution. Examples: invalid input given, resolver threw an error.
50+
2. `schema` – Only present if the [schema errors](#schema-errors) are being used. Any time a result field returns an error type.
51+
3. `other` – Anything else. Examples: network error during request, extension threw error, your anyware threw an error.
3452

35-
Return the standard graphql execution output. However, if there would be any errors then they're thrown as an `AggregateError`.
36-
This mode acts like you were using [`OrThrow`](#orthrow) method variants all the time.
53+
You can choose to output error categories in the following ways:
3754

38-
## `dataSuccess`
55+
1. `throw` – Errors from category will be thrown. There is no type safety with this approach.
56+
2. `return` – Errors from category will be returned. The return type will thus become a union type.
57+
3. `default` – Use whatever the default is (you can change the default).
3958

40-
Return just the data excluding [schema errors](#schema-errors). Errors are thrown as an `AggregateError`.
41-
This mode acts like you were using [`OrThrow`](#orthrow) method variants all the time.
59+
## Envelope
4260

43-
This mode is only available when using [schema errors](#schema-errors).
61+
You can choose to use an envelope. When you use an envelope the data will be returned in a `data` property. Additional metadata properties will be exposed:
4462

45-
## `data`
63+
1. `errors` – errors that you have chosen to include in the envelope.
64+
2. `extensions` – GraphQL execution result extensions.
65+
3. `response` – Only present if [transport](#link-todo) is `http`. The HTTP response to the request that was sent for the given GraphQL document.
4666

47-
Return just the data including [schema errors](#schema-errors) (if using). Other errors are thrown as an `AggregateError`.
67+
## Examples
4868

49-
**This mode is the default.**
69+
### Standard GraphQL
5070

51-
## `dataAndErrors`
71+
```ts
72+
const graffle = Graffle.create({
73+
output: {
74+
envelope: {
75+
errors: {
76+
execution: true, // Bring execution errors into envelope.
77+
},
78+
},
79+
errors: {
80+
other: 'throw',
81+
},
82+
},
83+
})
84+
85+
assertType<{
86+
data: {
87+
foo: string /* or whatever */
88+
}
89+
errors: GraphQLError[]
90+
extensions: unknown[]
91+
response: Response // Non-standard. Present when using HTTP transport.
92+
}>(await graffle.query.foo())
93+
```
5294

53-
Return a union type of data and errors. This is the most type-safe mode. It never throws.
95+
### Full Type Safety
96+
97+
```ts
98+
const graffle = Graffle.create({
99+
output: {
100+
defaults: {
101+
errorChannel: 'return',
102+
},
103+
envelope: false,
104+
},
105+
})
106+
107+
assertType<
108+
| string /* or whatever */
109+
| GraphQLError
110+
| Error
111+
>(await graffle.query.foo())
112+
```
54113

55114
# Schema Errors
56115

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,12 @@
111111
"@types/body-parser": "^1.19.5",
112112
"@types/express": "^4.17.21",
113113
"@types/json-bigint": "^1.0.4",
114-
"@types/node": "^22.0.0",
115-
"@typescript-eslint/eslint-plugin": "^7.18.0",
116-
"@typescript-eslint/parser": "^7.18.0",
114+
"@types/node": "^22.0.2",
115+
"@typescript-eslint/eslint-plugin": "^8.0.0",
116+
"@typescript-eslint/parser": "^8.0.0",
117117
"doctoc": "^2.2.1",
118118
"dripip": "^0.10.0",
119+
"es-toolkit": "^1.13.1",
119120
"eslint": "^9.8.0",
120121
"eslint-config-prisma": "^0.6.0",
121122
"eslint-plugin-deprecation": "^3.0.0",
@@ -133,10 +134,10 @@
133134
"jsdom": "^24.1.1",
134135
"json-bigint": "^1.0.0",
135136
"publint": "^0.2.9",
136-
"tsx": "^4.16.2",
137+
"tsx": "^4.16.5",
137138
"type-fest": "^4.23.0",
138139
"typescript": "^5.5.4",
139-
"typescript-eslint": "^7.18.0",
140-
"vitest": "^2.0.4"
140+
"typescript-eslint": "^8.0.0",
141+
"vitest": "^2.0.5"
141142
}
142143
}

0 commit comments

Comments
 (0)