@lewebsimple/nuxt-graphql 0.6.0 → 0.6.2
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
CHANGED
|
@@ -18,7 +18,7 @@ Opinionated Nuxt module that wires a typed GraphQL server + client into your app
|
|
|
18
18
|
- 🧠 **Type-safe helpers** for **queries, mutations, and subscriptions**, shared across **client + server**
|
|
19
19
|
- 🧊 **SSR-friendly** by default: request header forwarding + server-side schema execution helpers
|
|
20
20
|
- 🚀 **Client-side cache** for `useAsyncGraphQLQuery` (cache policies + optional persistence in localStorage)
|
|
21
|
-
- 🧯 **Unified error model** via `
|
|
21
|
+
- 🧯 **Unified error model** via `GraphQLExecutionResult` and `NormalizedError`
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
## Getting started
|
|
@@ -38,13 +38,12 @@ Declare your schemas, context, documents glob and optional client cache in [nuxt
|
|
|
38
38
|
export default defineNuxtConfig({
|
|
39
39
|
modules: ["@lewebsimple/nuxt-graphql"],
|
|
40
40
|
graphql: {
|
|
41
|
-
|
|
41
|
+
server: {
|
|
42
42
|
// Schemas to stitch together (local and/or remote)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
43
|
+
schema: {
|
|
44
|
+
// Local schema example
|
|
45
|
+
local: { type: "local", path: "server/graphql/schema.ts" },
|
|
46
|
+
|
|
48
47
|
// Remote schema example
|
|
49
48
|
swapi: {
|
|
50
49
|
type: "remote",
|
|
@@ -91,7 +90,7 @@ export default defineNuxtConfig({
|
|
|
91
90
|
```
|
|
92
91
|
|
|
93
92
|
|
|
94
|
-
### Define schema
|
|
93
|
+
### Define GraphQL schema (local and/or remote)
|
|
95
94
|
|
|
96
95
|
**Local schemas** must live under `server/` and export a `GraphQLSchema` as `schema`.
|
|
97
96
|
|
|
@@ -134,12 +133,14 @@ export const schema = createSchema<GraphQLContext>({
|
|
|
134
133
|
});
|
|
135
134
|
```
|
|
136
135
|
|
|
137
|
-
**Remote schemas** are introspected at build time from the
|
|
136
|
+
**Remote schemas** are introspected at build time from the endpoint URL and executed via an HTTP executor at runtime. Subscriptions are stripped from remote schemas.
|
|
137
|
+
|
|
138
|
+
The final schema is stitched from the all of the defined local / remote schemas.
|
|
138
139
|
|
|
139
140
|
|
|
140
|
-
### Define GraphQL context
|
|
141
|
+
### Define GraphQL context (optional)
|
|
141
142
|
|
|
142
|
-
Context
|
|
143
|
+
Context definition is optional and factories resolve in order on the server. Their return types are merged into a single `GraphQLContext` type which is exported from `#graphql/context`. You can use the auto-imported `defineGraphQLContext` helper for type-safety.
|
|
143
144
|
|
|
144
145
|
For example, create [server/graphql/context.ts](server/graphql/context.ts):
|
|
145
146
|
|
|
@@ -157,15 +158,10 @@ export default defineGraphQLContext(async (event) => {
|
|
|
157
158
|
|
|
158
159
|
### Write GraphQL documents (.gql)
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
By default, the module scans `**/*.gql` files for **named operations** and **fragments** which are converted into **types** and **typed document nodes** in `#graphql/operations`. The operations are exposed by name in `#graphql/registry` to allow type-safe execution with the provided **composables** and **server utils**.
|
|
161
162
|
|
|
162
163
|
⚠️ Operation names are required and must be unique.
|
|
163
164
|
|
|
164
|
-
By default, the module scans `**/*.gql` and generates:
|
|
165
|
-
|
|
166
|
-
- Typed documents and operations / fragments types in virtual modules under the `#graphql/operations` alias (internal)
|
|
167
|
-
- Operation registry in virtual modules under the `#graphql/registry` alias (internal)
|
|
168
|
-
|
|
169
165
|
Example document files:
|
|
170
166
|
|
|
171
167
|
```graphql
|
|
@@ -231,25 +227,13 @@ The auto-imported composables allow executing queries, mutations, and subscripti
|
|
|
231
227
|
|
|
232
228
|
```ts
|
|
233
229
|
// Cached query via useAsyncData
|
|
234
|
-
const { data, pending, error, refresh } = await useAsyncGraphQLQuery(
|
|
235
|
-
"HelloWorld",
|
|
236
|
-
undefined,
|
|
237
|
-
{
|
|
238
|
-
headers: {
|
|
239
|
-
"X-Request-Header": "request-header-value",
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
);
|
|
230
|
+
const { data, pending, error, refresh } = await useAsyncGraphQLQuery("HelloWorld", undefined);
|
|
243
231
|
|
|
244
232
|
// Direct HTTP query (SafeResult)
|
|
245
233
|
const { data: queryData, error: queryError } = await useGraphQLQuery("HelloWorld");
|
|
246
234
|
|
|
247
235
|
// Mutation (SafeResult)
|
|
248
|
-
const { mutate, pending: mutationPending } = useGraphQLMutation("Ping"
|
|
249
|
-
headers: {
|
|
250
|
-
"X-Request-Header": "request-header-value",
|
|
251
|
-
},
|
|
252
|
-
});
|
|
236
|
+
const { mutate, pending: mutationPending } = useGraphQLMutation("Ping");
|
|
253
237
|
const { data: mutationData, error: mutationError } = await mutate({ message: "Hello!" });
|
|
254
238
|
|
|
255
239
|
// Subscription (client-only, SSE)
|
|
@@ -257,30 +241,24 @@ const { data, error, start, stop } = useGraphQLSubscription("Time");
|
|
|
257
241
|
```
|
|
258
242
|
|
|
259
243
|
|
|
260
|
-
### Use the auto-imported server
|
|
244
|
+
### Use the auto-imported server utils
|
|
261
245
|
|
|
262
246
|
In server routes, you can execute queries and mutations directly against the stitched schema (no HTTP roundtrip):
|
|
263
247
|
|
|
264
248
|
```ts
|
|
265
249
|
export default defineEventHandler(async (event) => {
|
|
266
250
|
// Server-side GraphQL query example
|
|
267
|
-
const { data: queryData, error: queryError } = await
|
|
268
|
-
event,
|
|
269
|
-
"HelloWorld",
|
|
270
|
-
);
|
|
251
|
+
const { data: queryData, error: queryError } = await useGraphQLOperation(event, "HelloWorld" );
|
|
271
252
|
|
|
272
253
|
// Server-side GraphQL mutation example
|
|
273
|
-
const { data: mutationData } = await
|
|
274
|
-
event,
|
|
275
|
-
"Ping",
|
|
276
|
-
{ message: queryData?.hello ?? "fallback" },
|
|
254
|
+
const { data: mutationData } = await useGraphQLOperation(event, "Ping", { message: queryData?.hello ?? "Pong" },
|
|
277
255
|
);
|
|
278
256
|
|
|
279
257
|
return { queryData, mutationData, queryError };
|
|
280
258
|
});
|
|
281
259
|
```
|
|
282
260
|
|
|
283
|
-
Server helpers return a `
|
|
261
|
+
Server helpers return a `GraphQLExecutionResult` in the same format as some composables, i.e. `{ data: TResult, error: null } | { data: null, error: NormalizedError }`
|
|
284
262
|
|
|
285
263
|
|
|
286
264
|
### Query caching (client-side only)
|
|
@@ -293,10 +271,10 @@ Server helpers return a `SafeResult` in the same format as the client helpers.
|
|
|
293
271
|
|
|
294
272
|
#### Cache policies
|
|
295
273
|
|
|
296
|
-
- "no-cache"
|
|
297
|
-
- "cache-first"
|
|
298
|
-
- "network-first"
|
|
299
|
-
- "swr"
|
|
274
|
+
- `"no-cache"`: always fetches from the network (still dedupes in-flight).
|
|
275
|
+
- `"cache-first"`: returns cached value when present, otherwise fetches.
|
|
276
|
+
- `"network-first"`: tries the network first, falls back to cached value on error.
|
|
277
|
+
- `"swr"`: returns cached value immediately and refreshes in the background.
|
|
300
278
|
|
|
301
279
|
#### Per-query overrides
|
|
302
280
|
|
|
@@ -334,20 +312,16 @@ You can define custom logic around the remote executor for each remote schema by
|
|
|
334
312
|
For the example configuration above, create [server/graphql/swapi-hooks.ts](server/graphql/swapi-hooks.ts):
|
|
335
313
|
|
|
336
314
|
```ts
|
|
315
|
+
import { defu } from "defu";
|
|
316
|
+
|
|
337
317
|
export default defineRemoteExecutorHooks({
|
|
338
318
|
onRequest(request) {
|
|
339
|
-
|
|
340
|
-
request.extensions
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
};
|
|
345
|
-
},
|
|
346
|
-
onResult(result) {
|
|
347
|
-
console.log("Remote result", result);
|
|
348
|
-
},
|
|
349
|
-
onError(error) {
|
|
350
|
-
console.error("Remote error", error);
|
|
319
|
+
const { remoteAuthToken } = request.context || {};
|
|
320
|
+
request.extensions = defu(request.extensions, {
|
|
321
|
+
headers: {
|
|
322
|
+
"XAuthorization": `Bearer ${remoteAuthToken || ""}`,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
351
325
|
},
|
|
352
326
|
});
|
|
353
327
|
```
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -149,7 +149,7 @@ const executor = getRemoteExecutor({
|
|
|
149
149
|
hooks: [${hooksArray.join(", ")}],
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
const sdl =
|
|
152
|
+
const sdl = ${JSON.stringify(sdl)};
|
|
153
153
|
|
|
154
154
|
// SubschemaConfig exported for stitching
|
|
155
155
|
export const schema = {
|
|
@@ -203,7 +203,7 @@ function addUniversalTemplate({ filename, getContents, emitTs }) {
|
|
|
203
203
|
return modulePath;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
const version = "0.6.
|
|
206
|
+
const version = "0.6.2";
|
|
207
207
|
|
|
208
208
|
async function getDocuments(documentsGlob) {
|
|
209
209
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ExecutionRequest, ExecutionResult } from "@graphql-tools/utils";
|
|
2
|
+
import type { GraphQLContext } from "#graphql/context";
|
|
2
3
|
import { type HeadersInput } from "../../shared/lib/headers.js";
|
|
3
|
-
type
|
|
4
|
+
import type { GraphQLVariables } from "../../shared/lib/types.js";
|
|
5
|
+
type GraphQLExecutionRequest = ExecutionRequest<GraphQLVariables, GraphQLContext> & {
|
|
4
6
|
extensions?: {
|
|
5
7
|
headers?: HeadersInput;
|
|
6
8
|
};
|