@lewebsimple/nuxt-graphql 0.4.0 → 0.5.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.
Files changed (74) hide show
  1. package/README.md +134 -188
  2. package/dist/module.d.mts +74 -22
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +408 -369
  5. package/dist/runtime/app/composables/useAsyncGraphQLQuery.d.ts +19 -0
  6. package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +82 -0
  7. package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +10 -7
  8. package/dist/runtime/app/composables/useGraphQLCache.client.js +16 -31
  9. package/dist/runtime/app/composables/useGraphQLMutation.d.ts +10 -18
  10. package/dist/runtime/app/composables/useGraphQLMutation.js +7 -11
  11. package/dist/runtime/app/composables/useGraphQLQuery.d.ts +11 -9
  12. package/dist/runtime/app/composables/useGraphQLQuery.js +20 -78
  13. package/dist/runtime/app/composables/useGraphQLSubscription.client.d.ts +19 -0
  14. package/dist/runtime/app/composables/{useGraphQLSubscription.js → useGraphQLSubscription.client.js} +9 -14
  15. package/dist/runtime/app/lib/cache.d.ts +18 -0
  16. package/dist/runtime/app/lib/cache.js +9 -0
  17. package/dist/runtime/app/lib/execute-http.d.ts +14 -0
  18. package/dist/runtime/app/lib/execute-http.js +8 -0
  19. package/dist/runtime/app/lib/in-flight.d.ts +14 -0
  20. package/dist/runtime/app/lib/{graphql-cache.js → in-flight.js} +2 -7
  21. package/dist/runtime/app/lib/persisted.d.ts +26 -0
  22. package/dist/runtime/app/plugins/graphql-request.d.ts +7 -2
  23. package/dist/runtime/app/plugins/graphql-request.js +13 -14
  24. package/dist/runtime/app/plugins/graphql-sse.client.d.ts +7 -2
  25. package/dist/runtime/server/api/graphql.d.ts +8 -0
  26. package/dist/runtime/server/api/graphql.js +16 -0
  27. package/dist/runtime/server/lib/context.d.ts +10 -0
  28. package/dist/runtime/server/lib/context.js +3 -0
  29. package/dist/runtime/server/lib/default-context.d.ts +5 -0
  30. package/dist/runtime/server/lib/default-context.js +2 -0
  31. package/dist/runtime/server/lib/default-schema.d.ts +7 -0
  32. package/dist/runtime/server/lib/default-schema.js +18 -0
  33. package/dist/runtime/server/lib/execute-schema.d.ts +11 -0
  34. package/dist/runtime/server/lib/execute-schema.js +23 -0
  35. package/dist/runtime/server/lib/remote-executor.d.ts +29 -34
  36. package/dist/runtime/server/lib/remote-executor.js +27 -59
  37. package/dist/runtime/server/lib/schemas.d.ts +14 -0
  38. package/dist/runtime/server/lib/schemas.js +3 -0
  39. package/dist/runtime/server/lib/yoga.d.ts +6 -0
  40. package/dist/runtime/server/lib/yoga.js +20 -0
  41. package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +16 -8
  42. package/dist/runtime/server/utils/useServerGraphQLMutation.js +12 -11
  43. package/dist/runtime/server/utils/useServerGraphQLQuery.d.ts +16 -3
  44. package/dist/runtime/server/utils/useServerGraphQLQuery.js +16 -4
  45. package/dist/runtime/shared/lib/cache-config.d.ts +42 -0
  46. package/dist/runtime/{app → shared}/lib/cache-config.js +3 -3
  47. package/dist/runtime/shared/lib/error.d.ts +56 -0
  48. package/dist/runtime/shared/lib/error.js +61 -0
  49. package/dist/runtime/shared/lib/headers.d.ts +13 -3
  50. package/dist/runtime/shared/lib/headers.js +10 -35
  51. package/dist/runtime/shared/lib/registry.d.ts +12 -0
  52. package/dist/runtime/shared/lib/registry.js +8 -0
  53. package/dist/runtime/shared/lib/utils.d.ts +1 -0
  54. package/dist/runtime/shared/lib/utils.js +0 -0
  55. package/dist/runtime/{app → shared}/types/nuxt-graphql.d.ts +1 -5
  56. package/dist/types.d.mts +4 -0
  57. package/package.json +19 -33
  58. package/dist/helpers.d.mts +0 -3
  59. package/dist/helpers.mjs +0 -3
  60. package/dist/runtime/app/composables/useGraphQLSubscription.d.ts +0 -17
  61. package/dist/runtime/app/lib/cache-config.d.ts +0 -8
  62. package/dist/runtime/app/lib/graphql-cache.d.ts +0 -7
  63. package/dist/runtime/server/api/yoga-handler.d.ts +0 -2
  64. package/dist/runtime/server/api/yoga-handler.js +0 -42
  65. package/dist/runtime/server/lib/define-graphql-context.d.ts +0 -5
  66. package/dist/runtime/server/lib/define-graphql-context.js +0 -4
  67. package/dist/runtime/server/lib/define-remote-exec-middleware.d.ts +0 -30
  68. package/dist/runtime/server/lib/define-remote-exec-middleware.js +0 -3
  69. package/dist/runtime/server/lib/define-yoga-middleware.d.ts +0 -21
  70. package/dist/runtime/server/lib/define-yoga-middleware.js +0 -3
  71. package/dist/runtime/server/lib/execute-server-graphql.d.ts +0 -7
  72. package/dist/runtime/server/lib/execute-server-graphql.js +0 -34
  73. package/dist/runtime/shared/lib/graphql-error.d.ts +0 -17
  74. package/dist/runtime/shared/lib/graphql-error.js +0 -28
package/README.md CHANGED
@@ -13,12 +13,12 @@ Opinionated Nuxt module that wires a typed GraphQL server + client into your app
13
13
  ## Features
14
14
 
15
15
  - 🧘 **GraphQL Yoga** server at `/api/graphql` (**GraphiQL** in dev) + **SSE subscriptions**
16
- - 🪡 **Stitched schema** from local and/or remote schemas (remote introspection at build time)
17
- - 🪄 Code generation from `.gql` documents → **typed documents nodes**
18
- - 🧠 **Type-safe GraphQL helpers** for **queries, mutations, and subscriptions**, driven by **operation names** and shared across **client + server routes**
19
- - 🧊 **SSR-friendly** by default: request header/cookie forwarding + no-HTTP server execution helpers
20
- - 🚀 **Query caching** for `useGraphQLQuery` (cache policies + optional persistence in localStorage)
21
- - 🪝 **Optional hooks**: Yoga middleware + per-remote executor middleware + client error hook (`graphql:error`)
16
+ - 🪡 **Stitched schema** from local and/or remote schemas (remote introspection at build time; subscriptions stripped)
17
+ - 🪄 Code generation from `.gql` documents → **typed operation documents** + **registry**
18
+ - 🧠 **Type-safe helpers** for **queries, mutations, and subscriptions**, shared across **client + server**
19
+ - 🧊 **SSR-friendly** by default: request header forwarding + server-side schema execution helpers
20
+ - 🚀 **Client-side cache** for `useAsyncGraphQLQuery` (cache policies + optional persistence in localStorage)
21
+ - 🧯 **Unified error model** via `SafeResult` and `NormalizedError`
22
22
 
23
23
 
24
24
  ## Getting started
@@ -26,80 +26,83 @@ Opinionated Nuxt module that wires a typed GraphQL server + client into your app
26
26
  Install the module to your Nuxt application with one command:
27
27
 
28
28
  ```bash
29
- pnpx nuxi module add @lewebsimple/nuxt-graphql
29
+ pnpx nuxt module add @lewebsimple/nuxt-graphql
30
30
  ```
31
31
 
32
32
 
33
33
  ### Configuration
34
34
 
35
- Declare your schemas, context, documents glob and any optional middleware in `nuxt.config.ts`:
35
+ Declare your schemas, context, documents glob and optional client cache in [nuxt.config.ts](nuxt.config.ts):
36
36
 
37
37
  ```ts
38
38
  export default defineNuxtConfig({
39
39
  modules: ["@lewebsimple/nuxt-graphql"],
40
40
  graphql: {
41
- // Schemas to stitch together (local and/or remote)
42
- schemas: {
43
- local: {
44
- type: "local",
45
- path: "server/graphql/schema.ts",
46
- },
47
- // Remote schema example
48
- swapi: {
49
- type: "remote",
50
- url: "https://swapi-graphql.netlify.app/graphql",
51
- // Optional: static headers for this remote
52
- headers: {
53
- "X-Static-Header": "static-header-value",
41
+ yoga: {
42
+ // Schemas to stitch together (local and/or remote)
43
+ schemas: {
44
+ local: {
45
+ type: "local",
46
+ path: "server/graphql/schema.ts",
47
+ },
48
+ // Remote schema example
49
+ swapi: {
50
+ type: "remote",
51
+ url: "https://swapi-graphql.netlify.app/graphql",
52
+ // Optional: static headers for this remote
53
+ headers: {
54
+ "X-Static-Header": "static-header-value",
55
+ },
56
+ // Optional: per-remote execution hooks
57
+ hooks: ["server/graphql/swapi-hooks.ts"],
54
58
  },
55
- // Optional: per-remote execution middleware (onRequest / onResponse / onError hooks)
56
- middleware: "server/graphql/swapi-middleware.ts",
57
59
  },
60
+
61
+ // Optional: custom GraphQL context factories (defaults to [])
62
+ context: ["server/graphql/context.ts"],
58
63
  },
59
-
60
- // Optional: custom GraphQL context (defaults to {})
61
- context: "server/graphql/context.ts",
62
-
63
- // Optional: documents glob (defaults to **/*.gql)
64
- documents: "**/*.gql",
65
-
66
- // Optional: Yoga middleware (onRequest / onResponse hooks)
67
- middleware: "server/graphql/yoga-middleware.ts",
68
-
69
- // Optional: query caching (client-side only)
70
- // - In-memory cache uses Nuxt `useAsyncData`/`useNuxtData`
71
- // - Persistence in localStorage is enabled when `ttl` is set
72
- // - Version / prefix allow cache invalidation accross deployments
73
- cache: {
74
- cachePolicy: "cache-first", // "no-cache" | "cache-first" | "network-first" | "swr"
75
- cacheVersion: "1",
76
- keyPrefix: "gql",
77
- // Persist cache entries in localStorage with TTL in seconds
78
- // - 0 = never expires
79
- // - undefined = persistence disabled
80
- ttl: 60,
64
+
65
+ client: {
66
+ // Optional: documents glob (defaults to **/*.gql)
67
+ documents: "**/*.gql",
68
+
69
+ // Optional: headers forwarded from SSR to graphql-request (defaults to ["authorization", "cookie"])
70
+ ssrForwardHeaders: ["authorization", "cookie"],
71
+
72
+ // Optional: query caching (client-side only, for useAsyncGraphQLQuery)
73
+ cache: {
74
+ policy: "cache-first", // "no-cache" | "cache-first" | "network-first" | "swr"
75
+ keyVersion: "1",
76
+ keyPrefix: "gql",
77
+ // Persist cache entries in localStorage with TTL in seconds
78
+ // - 0 = never expires
79
+ // - undefined = persistence disabled
80
+ ttl: 60,
81
+ },
81
82
  },
82
83
 
83
- // Optional: save path for the generated stitched SDL (defaults to .nuxt/graphql/schema.graphql)
84
- sdl: "server/graphql/schema.graphql",
84
+ // Optional: save path for the stitched SDL (defaults to "server/graphql/schema.graphql")
85
+ saveSDL: "server/graphql/schema.graphql",
86
+
87
+ // Optional: save path for the generated GraphQL config (defaults to "graphql.config.json")
88
+ saveConfig: "graphql.config.json",
85
89
  },
86
90
  });
87
91
  ```
88
92
 
89
93
 
90
- ### Define your schema(s) (local and/or remote)
94
+ ### Define schema(s) (local and/or remote)
91
95
 
92
- **Local schemas** must be located inside `server/` and export an executable `GraphQLSchema` using the tool of your choice (graphql-yoga, Pothos, etc).
96
+ **Local schemas** must live under `server/` and export a `GraphQLSchema` as `schema`. You can use the provided `defineGraphQLSchema` helper for type-safety.
93
97
 
94
- ⚠️ Using auto-imported utilities or importing from aliases might break code generation.
95
-
96
- For the example configuration above, create `server/graphql/schema.ts`:
98
+ For the example configuration above, create [server/graphql/schema.ts](server/graphql/schema.ts):
97
99
 
98
100
  ```ts
99
101
  import { createSchema } from "graphql-yoga";
102
+ import { defineGraphQLSchema } from "@lewebsimple/nuxt-graphql";
100
103
  import type { GraphQLContext } from "#graphql/context";
101
104
 
102
- export const schema = createSchema<GraphQLContext>({
105
+ const schema = createSchema<GraphQLContext>({
103
106
  typeDefs: /* GraphQL */ `
104
107
  type Query {
105
108
  hello: String!
@@ -130,19 +133,21 @@ export const schema = createSchema<GraphQLContext>({
130
133
  },
131
134
  },
132
135
  });
136
+
137
+ export default defineGraphQLSchema({ schema });
133
138
  ```
134
139
 
135
- **Remote schemas** are introspected at build time from the required endpoint URL. Each remote can be configured with optional static headers and remote execution middleware (see configuration above).
140
+ **Remote schemas** are introspected at build time from the required endpoint URL and executed via an HTTP executor at runtime. Subscriptions are stripped from remote schemas.
136
141
 
137
142
 
138
- ### Define a type-safe GraphQL context (optional)
143
+ ### Define GraphQL context factories (optional)
139
144
 
140
- The GraphQL context can be user-defined with the provided `defineGraphQLContext` helper. This ensures proper server-side typing by inferring the type from the return value of the context creation function. The `GraphQLContext` type can be imported from the `#graphql/context` server alias.
145
+ Context factories are optional and run on the server in order. Their return types are merged into a single `GraphQLContext` type.
141
146
 
142
- For example, providing the user from the `nuxt-auth-utils` session with the configuration above, create `server/graphql/context.ts`:
147
+ For example, create [server/graphql/context.ts](server/graphql/context.ts):
143
148
 
144
149
  ```ts
145
- import { defineGraphQLContext } from "@lewebsimple/nuxt-graphql/helpers";
150
+ import { defineGraphQLContext } from "@lewebsimple/nuxt-graphql";
146
151
  import { getUserSession } from "nuxt-auth-utils";
147
152
 
148
153
  export default defineGraphQLContext(async (event) => {
@@ -154,19 +159,20 @@ export default defineGraphQLContext(async (event) => {
154
159
  ```
155
160
 
156
161
 
157
- ### Write GraphQL documents (`.gql`)
162
+ ### Write GraphQL documents (.gql)
158
163
 
159
- Write operations in `.gql` document files; operation names become registry keys like `useGraphQLQuery("HelloWorld")`.
164
+ Write operations in `.gql` files; operation names become registry keys like `useGraphQLQuery("HelloWorld")`.
160
165
 
161
166
  ⚠️ Operation names are required and must be unique.
162
167
 
163
168
  By default, the module scans `**/*.gql` and generates:
164
169
 
165
- - Typed documents in `#graphql/typed-documents`
166
- - Operation registry by name in `#graphql/registry` (used internally)
167
- - A `graphql.config.json` at the project root (editor tooling)
170
+ - Typed documents and types in virtual modules under the `#graphql/operations` alias (internal)
171
+ - Operation registry in virtual modules under the `#graphql/registry` alias (internal)
172
+ - Fragment types in virtual modules under the `#graphql/fragments` alias
173
+
174
+ Example document files:
168
175
 
169
- Example document files (filenaming convention can vary):
170
176
  ```graphql
171
177
  # app/graphql/HelloWorld.query.gql
172
178
  query HelloWorld {
@@ -190,15 +196,14 @@ subscription Time {
190
196
 
191
197
  That's it! You can now use Nuxt GraphQL in your Nuxt app ✨
192
198
 
199
+
193
200
  ### Fragments
194
201
 
195
202
  Fragments are fully supported and are the recommended way to share selection sets across operations.
196
203
 
197
204
  - Fragment names must be unique across all `.gql` files (duplicates throw during generation).
198
- - Fragments are generated into `#graphql/typed-documents` by GraphQL Codegen:
199
- - a TypeScript type like `TheFilmFragment`
200
- - and a document constant like `TheFilmFragmentDoc`
201
- - Fragments are **not executable by themselves** (GraphQL requires an operation), and they are **not part of the `#graphql/registry`**. The auto-imported composables and server utilities only accept operation names (`query` / `mutation` / `subscription`).
205
+ - Fragment types are re-exported from `#graphql/fragments`.
206
+ - Fragments are not executable by themselves and are not part of the registry.
202
207
 
203
208
  Example with a fragment:
204
209
 
@@ -218,108 +223,92 @@ query SwapiFilms {
218
223
  }
219
224
  ```
220
225
 
221
- From TypeScript, you can also use fragment types explicitly when you need them:
226
+ From TypeScript, you can also use fragment types explicitly when needed:
222
227
 
223
228
  ```ts
224
- import type { TheFilmFragment } from "#graphql/typed-documents";
229
+ import type { TheFilmFragment } from "#graphql/fragments";
225
230
  ```
226
231
 
227
232
 
228
233
  ### Use the auto-imported composables
229
234
 
230
- The auto-imported operation composables allow executing **queries**, **mutations** and **subscriptions** based on their registry name with full type-safety (variables and return value).
235
+ The auto-imported composables allow executing queries, mutations, and subscriptions based on their registry name with full type-safety (variables and return value).
231
236
 
232
237
  ```ts
233
- // Query (useAsyncData under the hood)
234
- const { data, pending, error, refresh } = await useGraphQLQuery(
235
- "HelloWorld", // Registry name, i.e. "query HelloWorld { hello }"
236
- undefined, // Type-safe variables
238
+ // Cached query via useAsyncData
239
+ const { data, pending, error, refresh } = await useAsyncGraphQLQuery(
240
+ "HelloWorld",
241
+ undefined,
237
242
  {
238
- // Custom request headers
239
243
  headers: {
240
- "X-Request-Header": "request-header-value"
244
+ "X-Request-Header": "request-header-value",
241
245
  },
242
- // Additional useAsyncData options
243
- // lazy: true,
244
246
  },
245
247
  );
246
248
 
247
- // Mutation
248
- const { mutate } = useGraphQLMutation("Ping", {
249
- // Custom request headers
250
- headers: {
251
- "X-Request-Header": "request-header-value"
252
- },
253
- });
254
- const { data, pending, error } = await mutate({ message: "Hello from ping mutation!" }, {
255
- // Each `mutate` call may send additional headers
249
+ // Direct HTTP query (SafeResult)
250
+ const { data: queryData, error: queryError } = await useGraphQLQuery("HelloWorld");
251
+
252
+ // Mutation (SafeResult)
253
+ const { mutate, pending: mutationPending } = useGraphQLMutation("Ping", {
256
254
  headers: {
257
- "X-Mutate-Header": "mutate-header-value",
255
+ "X-Request-Header": "request-header-value",
258
256
  },
259
257
  });
258
+ const { data: mutationData, error: mutationError } = await mutate({ message: "Hello!" });
260
259
 
261
260
  // Subscription (client-only, SSE)
262
261
  const { data, error, start, stop } = useGraphQLSubscription("Time");
263
- // data and error are shallowRef
264
262
  ```
265
263
 
264
+
266
265
  ### Use the auto-imported server-side utilities
267
266
 
268
- In server routes, you can execute **queries** and **mutations** directly against the stitched schema (no HTTP roundtrip):
267
+ In server routes, you can execute queries and mutations directly against the stitched schema (no HTTP roundtrip):
269
268
 
270
269
  ```ts
271
270
  export default defineEventHandler(async (event) => {
272
271
  // Server-side GraphQL query example
273
- const { hello } = await useServerGraphQLQuery(event, "HelloWorld", undefined, {
274
- headers: {
275
- "X-Server-Header": "server-header-value",
276
- },
277
- });
272
+ const { data: queryData, error: queryError } = await useServerGraphQLQuery(
273
+ event,
274
+ "HelloWorld",
275
+ );
278
276
 
279
277
  // Server-side GraphQL mutation example
280
- const { mutate } = useServerGraphQLMutation(event, "Ping", {
281
- headers: {
282
- "X-Server-Header": "server-header-value",
283
- },
284
- });
285
- const { ping } = await mutate({ message: hello }, {
286
- headers: {
287
- "X-Mutation-Header": "mutation-header-value",
288
- },
289
- });
278
+ const { data: mutationData } = await useServerGraphQLMutation(
279
+ event,
280
+ "Ping",
281
+ { message: queryData?.hello ?? "fallback" },
282
+ );
290
283
 
291
- return { ping };
284
+ return { queryData, mutationData, queryError };
292
285
  });
293
286
  ```
294
287
 
295
- The function signature is almost identical to the auto-imported composables, except you need to pass `event` as the first argument (and of course queries don't rely on `useAsyncData`).
296
-
297
- Also, resulting data is returned directly from `useServerGraphQLQuery` and `mutate` (throws an error on failure).
288
+ Server helpers return a `SafeResult` in the same format as the client helpers.
298
289
 
299
290
 
300
291
  ### Query caching (client-side only)
301
292
 
302
- `useGraphQLQuery` can cache **query results** based on the global cache configuration (see configuration above) and per-query overrides (see below).
293
+ `useAsyncGraphQLQuery` can cache query results based on the global cache configuration and per-query overrides.
303
294
 
304
- - In-flight requests are **deduplicated** (same operation + variables → one network call).
305
- - **In-memory** cache uses Nuxt `useAsyncData`/`useNuxtData`.
306
- - **Persisted** cache stores entries in `localStorage` for `ttl` seconds (`0` = never expires).
295
+ - In-flight requests are deduplicated (same operation + variables → one network call).
296
+ - In-memory cache uses Nuxt `useAsyncData`/`useNuxtData`.
297
+ - Persisted cache stores entries in localStorage for ttl seconds (0 = never expires).
307
298
 
308
299
  #### Cache policies
309
300
 
310
- - `"no-cache"`: always fetches from the network (still dedupes in-flight).
311
- - `"cache-first"`: returns cached value when present, otherwise fetches.
312
- - `"network-first"`: tries the network first, falls back to cached value on error.
313
- - `"swr"` (stale-while-revalidate): returns cached value immediately (when present) and refreshes in the background.
301
+ - "no-cache": always fetches from the network (still dedupes in-flight).
302
+ - "cache-first": returns cached value when present, otherwise fetches.
303
+ - "network-first": tries the network first, falls back to cached value on error.
304
+ - "swr": returns cached value immediately and refreshes in the background.
314
305
 
315
306
  #### Per-query overrides
316
307
 
317
- Caching configuration can be overridden per-query:
318
-
319
308
  ```ts
320
- const { data } = await useGraphQLQuery("HelloWorld", undefined, {
309
+ const { data } = await useAsyncGraphQLQuery("HelloWorld", undefined, {
321
310
  cache: {
322
- cachePolicy: "network-first",
311
+ policy: "network-first",
323
312
  ttl: undefined, // disable persistence for this call
324
313
  },
325
314
  });
@@ -327,92 +316,49 @@ const { data } = await useGraphQLQuery("HelloWorld", undefined, {
327
316
 
328
317
  #### Manual invalidation
329
318
 
330
- On the client, `useGraphQLCache()` is used to invalidate in-memory and/or persisted entries:
319
+ On the client, `useGraphQLCache()` can invalidate in-memory and persisted entries:
331
320
 
332
321
  ```ts
333
- const { invalidateByKey, invalidateByOperation, invalidateAll } = useGraphQLCache();
322
+ const { invalidate } = useGraphQLCache();
334
323
 
335
324
  // Invalidate a single entry (operation + variables)
336
- await invalidateByKey("HelloWorld", {});
325
+ await invalidate({ operation: "HelloWorld", variables: {} });
337
326
 
338
- // Invalidate all entries for an operation (all variables)
339
- await invalidateByOperation("HelloWorld");
340
-
341
- // Optional: target a specific layer
342
- await invalidateByOperation("HelloWorld", { layer: "persisted" });
327
+ // Invalidate all entries for an operation
328
+ await invalidate({ operation: "HelloWorld" });
343
329
 
344
330
  // Invalidate all entries
345
- await invalidateAll();
346
- ```
347
-
348
-
349
- ### Yoga middleware (optional)
350
-
351
- You can define custom logic around the Yoga event handler by using the provided `defineYogaMiddleware` helper with the following hooks:
352
- - `onRequest` runs before Yoga handles the request;
353
- - `onResponse` runs after and can replace the outgoing `Response` via `setResponse`.
354
-
355
- For the example configuration above, create `server/graphql/yoga-middleware.ts`:
356
-
357
- ```ts
358
- import { defineYogaMiddleware } from "@lewebsimple/nuxt-graphql/helpers";
359
- import { getUserSession } from "nuxt-auth-utils";
360
-
361
- export default defineYogaMiddleware({
362
- async onRequest({ event, context, request }) {
363
- const session = await getUserSession(event);
364
- if (!session?.user) {
365
- throw createError({ statusCode: 401, message: "Unauthorized" });
366
- }
367
- },
368
- async onResponse({ event, context, request, response, setResponse }) {
369
- setHeader(event, "X-Custom-Yoga-Middleware-Response-Header", "my-custom-value");
370
- },
371
- });
331
+ await invalidate();
372
332
  ```
373
333
 
374
- ### Remote executor middleware (optional, per remote)
375
334
 
376
- You can define custom logic around the remote executor (from `@graphql-tools/utils`) for each one of your remote schema by using the provided `defineRemoteExecMiddleware` helper with the following hooks:
377
- - `onRequest` runs before the fetch (headers are mutable);
378
- - `onResponse` runs after an OK response before JSON parsing (cloned response);
379
- - `onError` runs for non-2xx responses, GraphQL errors returned in the payload, JSON parse failures, or network errors (the `response` can be `undefined` in that last case).
335
+ ### Remote executor hooks (optional, per remote)
380
336
 
381
- ⚠️ Remote executor middlewares don't have access to the H3 `event` handled by Yoga since they are executed in the context of the delegated subschema resolution.
337
+ You can define custom logic around the remote executor for each remote schema by using the provided `defineRemoteExecutorHooks` helper.
382
338
 
383
- For the example configuration above, create `server/graphql/swapi-middleware.ts`:
339
+ For the example configuration above, create [server/graphql/swapi-hooks.ts](server/graphql/swapi-hooks.ts):
384
340
 
385
341
  ```ts
386
- import { defineRemoteExecMiddleware } from "@lewebsimple/nuxt-graphql";
387
-
388
- export default defineRemoteExecMiddleware({
389
- onRequest({ remoteName, operationName, context, fetchOptions }) {
390
- console.log(`SWAPI Request Middleware [${remoteName} - ${operationName}]`);
391
- fetchOptions.headers.set("X-Remote-Exec-Request-Header", "custom-value");
342
+ import { defineRemoteExecutorHooks } from "@lewebsimple/nuxt-graphql";
343
+
344
+ export default defineRemoteExecutorHooks({
345
+ onRequest(request) {
346
+ // Dynamically inject headers
347
+ request.extensions ??= {};
348
+ request.extensions.headers = {
349
+ ...request.extensions.headers,
350
+ "X-Remote-Exec-Request-Header": "custom-value",
351
+ };
392
352
  },
393
- onResponse({ remoteName, operationName, context, response }) {
394
- console.log(`SWAPI Response Middleware [${remoteName} - ${operationName}]`);
353
+ onResult(result) {
354
+ console.log("Remote result", result);
395
355
  },
396
- onError({ remoteName, operationName, error }) {
397
- console.error(`SWAPI Error Middleware [${remoteName} - ${operationName}]:`, error);
356
+ onError(error) {
357
+ console.error("Remote error", error);
398
358
  },
399
359
  });
400
360
  ```
401
361
 
402
- ### Client error hook (optional)
403
-
404
- Handle normalized GraphQL errors globally on the client (toast, logging, etc.).
405
-
406
- The client calls the `graphql:error` Nuxt hook when a `graphql-request` response contains errors:
407
-
408
- ```ts
409
- export default defineNuxtPlugin((nuxtApp) => {
410
- nuxtApp.hook("graphql:error", (error) => {
411
- console.error("GraphQL error", error);
412
- });
413
- });
414
- ```
415
-
416
362
 
417
363
  ## Contribution
418
364
 
package/dist/module.d.mts CHANGED
@@ -1,36 +1,88 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { CacheConfig } from '../dist/runtime/app/lib/cache-config.js';
2
+ import { HeadersInput } from '../dist/runtime/shared/lib/headers.js';
3
+ import { CacheConfig } from '../dist/runtime/shared/lib/cache-config.js';
4
+ export { defineGraphQLContext } from '../dist/runtime/server/lib/context.js';
5
+ export { defineRemoteExecutorHooks } from '../dist/runtime/server/lib/remote-executor.js';
3
6
 
4
- /**
5
- * Find multiple files across directories
6
- *
7
- * @param dirs Directories to search in
8
- * @param pattern Glob pattern relative to each directory
9
- * @returns Array of found file paths
10
- */
11
- type GlobPattern = string | string[];
12
-
13
- type LocalSchemaDef = {
7
+ interface LocalSchemaDef {
14
8
  type: "local";
9
+ /**
10
+ * Path to a server-side module exporting
11
+ * `export default defineLocalGraphQLSchema({ schema })`
12
+ * Resolved from rootDir.
13
+ */
15
14
  path: string;
16
- };
17
- type RemoteSchemaDef = {
15
+ }
16
+ interface RemoteSchemaDef {
18
17
  type: "remote";
18
+ /**
19
+ * Remote GraphQL endpoint.
20
+ * Used for build-time introspection and runtime delegation.
21
+ */
19
22
  url: string;
20
- headers?: HeadersInit;
21
- middleware?: string;
22
- };
23
+ /**
24
+ * Static headers applied to all delegated requests.
25
+ * `null` unsets a header.
26
+ */
27
+ headers?: HeadersInput;
28
+ /**
29
+ * Paths to remote execution hook modules.
30
+ * Resolved from rootDir.
31
+ */
32
+ hooks?: string[];
33
+ }
23
34
  type SchemaDef = LocalSchemaDef | RemoteSchemaDef;
24
35
 
25
36
  interface NuxtGraphQLModuleOptions {
26
- schemas: Record<string, SchemaDef>;
27
- context?: string;
28
- documents?: GlobPattern;
37
+ /**
38
+ * Client-side GraphQL configuration (HTTP + cache).
39
+ */
40
+ client?: {
41
+ /**
42
+ * Global cache configuration for queries.
43
+ */
44
+ cache?: Partial<CacheConfig>;
45
+ /**
46
+ * GraphQL documents glob pattern.
47
+ * Default: "**\/*.gql"
48
+ */
49
+ documents?: string;
50
+ /**
51
+ * Headers forwarded from the SSR request to graphql-request.
52
+ * Default: ["authorization", "cookie"]
53
+ */
54
+ ssrForwardHeaders?: string[];
55
+ };
56
+ /**
57
+ * Where to write graphql.config.json.
58
+ * Resolved from rootDir.
59
+ * Default: ./graphql.config.json
60
+ */
29
61
  saveConfig?: string;
30
- saveSdl?: string;
31
- middleware?: string;
32
- cache?: Partial<CacheConfig>;
62
+ /**
63
+ * Where to write the stitched GraphQL SDL.
64
+ * Resolved from rootDir.
65
+ * Default: server/graphql/schema.graphql
66
+ */
67
+ saveSDL?: string;
68
+ /**
69
+ * Server-side GraphQL configuration (Yoga + execution).
70
+ */
71
+ yoga?: {
72
+ /**
73
+ * Paths to GraphQL context factories.
74
+ * Must live in server/.
75
+ * Resolved from rootDir.
76
+ */
77
+ context?: string[];
78
+ /**
79
+ * GraphQL schemas to stitch.
80
+ * Key = schemaName.
81
+ */
82
+ schemas?: Record<string, SchemaDef>;
83
+ };
33
84
  }
85
+
34
86
  declare const _default: _nuxt_schema.NuxtModule<NuxtGraphQLModuleOptions, NuxtGraphQLModuleOptions, false>;
35
87
 
36
88
  export { _default as default };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "@lewebsimple/nuxt-graphql",
2
+ "name": "nuxt-graphql",
3
3
  "configKey": "graphql",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"