@lewebsimple/nuxt-graphql 0.5.13 → 0.6.1
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 +31 -57
- package/dist/module.d.mts +5 -38
- package/dist/module.json +2 -2
- package/dist/module.mjs +49 -155
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.d.ts +7 -7
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +12 -8
- package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +9 -1
- package/dist/runtime/app/composables/useGraphQLCache.client.js +1 -1
- package/dist/runtime/app/composables/useGraphQLMutation.d.ts +3 -12
- package/dist/runtime/app/composables/useGraphQLMutation.js +11 -10
- package/dist/runtime/app/composables/useGraphQLQuery.d.ts +2 -11
- package/dist/runtime/app/composables/useGraphQLQuery.js +11 -21
- package/dist/runtime/app/composables/useGraphQLSubscription.client.d.ts +1 -1
- package/dist/runtime/app/composables/useGraphQLSubscription.client.js +1 -1
- package/dist/runtime/{shared → app}/lib/cache.d.ts +9 -3
- package/dist/runtime/{shared → app}/lib/cache.js +3 -3
- package/dist/runtime/app/plugins/execute-graphql.d.ts +25 -0
- package/dist/runtime/app/plugins/execute-graphql.js +25 -0
- package/dist/runtime/app/plugins/graphql-sse.client.d.ts +12 -2
- package/dist/runtime/server/api/graphql.d.ts +0 -6
- package/dist/runtime/server/api/graphql.js +4 -5
- package/dist/runtime/server/lib/execute-graphql-schema.d.ts +3 -0
- package/dist/runtime/server/lib/execute-graphql-schema.js +22 -0
- package/dist/runtime/server/lib/remote-executor.d.ts +22 -12
- package/dist/runtime/server/lib/remote-executor.js +17 -18
- package/dist/runtime/server/lib/yoga.d.ts +1 -2
- package/dist/runtime/server/lib/yoga.js +2 -4
- package/dist/runtime/server/tsconfig.json +2 -2
- package/dist/runtime/server/utils/defineRemoteExecutorHooks.d.ts +1 -6
- package/dist/runtime/server/utils/useGraphQLOperation.d.ts +16 -0
- package/dist/runtime/server/utils/useGraphQLOperation.js +12 -0
- package/dist/runtime/shared/lib/error.d.ts +7 -21
- package/dist/runtime/shared/lib/error.js +1 -18
- package/dist/runtime/shared/lib/headers.d.ts +2 -1
- package/dist/runtime/shared/lib/headers.js +1 -1
- package/dist/runtime/shared/lib/types.d.ts +16 -0
- package/dist/runtime/shared/utils/execute-graphql-http.d.ts +7 -0
- package/dist/runtime/shared/utils/execute-graphql-http.js +31 -0
- package/package.json +3 -9
- package/dist/runtime/app/lib/execute-http.d.ts +0 -13
- package/dist/runtime/app/lib/execute-http.js +0 -8
- package/dist/runtime/app/plugins/graphql-request.d.ts +0 -12
- package/dist/runtime/app/plugins/graphql-request.js +0 -20
- package/dist/runtime/server/lib/default-schema.d.ts +0 -4
- package/dist/runtime/server/lib/default-schema.js +0 -16
- package/dist/runtime/server/lib/execute-schema.d.ts +0 -11
- package/dist/runtime/server/lib/execute-schema.js +0 -23
- package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +0 -17
- package/dist/runtime/server/utils/useServerGraphQLMutation.js +0 -14
- package/dist/runtime/server/utils/useServerGraphQLQuery.d.ts +0 -17
- package/dist/runtime/server/utils/useServerGraphQLQuery.js +0 -14
- package/dist/runtime/shared/lib/utils.d.ts +0 -1
- /package/dist/runtime/shared/lib/{utils.js → types.js} +0 -0
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.d.mts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { HeadersInput } from '../dist/runtime/shared/lib/headers.js';
|
|
3
|
+
import { CacheConfig } from '../dist/runtime/app/lib/cache.js';
|
|
2
4
|
|
|
3
5
|
type LocalSchemaDef = {
|
|
4
6
|
type: "local";
|
|
@@ -13,52 +15,17 @@ type RemoteSchemaDef = {
|
|
|
13
15
|
type SchemaDef = LocalSchemaDef | RemoteSchemaDef;
|
|
14
16
|
|
|
15
17
|
interface NuxtGraphQLModuleOptions {
|
|
16
|
-
/**
|
|
17
|
-
* Client-side GraphQL configuration (HTTP + cache).
|
|
18
|
-
*/
|
|
19
18
|
client?: {
|
|
20
|
-
/**
|
|
21
|
-
* Global cache configuration for queries.
|
|
22
|
-
*/
|
|
23
|
-
cache?: Partial<GraphQLCacheConfig>;
|
|
24
|
-
/**
|
|
25
|
-
* GraphQL documents glob pattern.
|
|
26
|
-
* Default: "**\/*.gql"
|
|
27
|
-
*/
|
|
28
19
|
documents?: string;
|
|
29
|
-
|
|
30
|
-
* Headers forwarded from the SSR request to graphql-request.
|
|
31
|
-
* Default: ["authorization", "cookie"]
|
|
32
|
-
*/
|
|
20
|
+
cache?: Partial<CacheConfig>;
|
|
33
21
|
ssrForwardHeaders?: string[];
|
|
34
22
|
};
|
|
35
|
-
/**
|
|
36
|
-
* Where to write graphql.config.json.
|
|
37
|
-
* Resolved from rootDir.
|
|
38
|
-
* Default: ./graphql.config.json
|
|
39
|
-
*/
|
|
40
|
-
saveConfig?: string;
|
|
41
|
-
/**
|
|
42
|
-
* Where to write the stitched GraphQL SDL.
|
|
43
|
-
* Resolved from rootDir.
|
|
44
|
-
* Default: server/graphql/schema.graphql
|
|
45
|
-
*/
|
|
46
|
-
saveSDL?: string;
|
|
47
|
-
/**
|
|
48
|
-
* Server-side GraphQL configuration (Yoga server + execution).
|
|
49
|
-
*/
|
|
50
23
|
server?: {
|
|
51
|
-
/**
|
|
52
|
-
* Paths to GraphQL server context factories relative to rootDir.
|
|
53
|
-
* export default defineGraphQLContext((event: H3Event) => Promise<Record<string, unknown>>)
|
|
54
|
-
*/
|
|
55
24
|
context?: string[];
|
|
56
|
-
/**
|
|
57
|
-
* GraphQL schema definition.
|
|
58
|
-
* Key = schemaName.
|
|
59
|
-
*/
|
|
60
25
|
schema?: Record<string, SchemaDef>;
|
|
61
26
|
};
|
|
27
|
+
saveConfig?: string;
|
|
28
|
+
saveSDL?: string;
|
|
62
29
|
}
|
|
63
30
|
declare const _default: _nuxt_schema.NuxtModule<NuxtGraphQLModuleOptions, NuxtGraphQLModuleOptions, false>;
|
|
64
31
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,17 +2,17 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { relative, resolve, dirname } from 'node:path';
|
|
3
3
|
import { defu } from 'defu';
|
|
4
4
|
import { stitchSchemas } from '@graphql-tools/stitch';
|
|
5
|
-
import {
|
|
5
|
+
import { addTemplate, addServerTemplate, defineNuxtModule, useLogger, createResolver, addServerHandler, addPlugin, addImportsDir, addServerImportsDir } from '@nuxt/kit';
|
|
6
6
|
import { hash } from 'ohash';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import { printSchema, lexicographicSortSchema, buildSchema, getIntrospectionQuery, buildClientSchema, GraphQLSchema, Kind } from 'graphql';
|
|
9
|
-
import { resolveCacheConfig } from '../dist/runtime/shared/lib/cache.js';
|
|
10
9
|
import { loadDocuments } from '@graphql-tools/load';
|
|
11
10
|
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
|
|
12
11
|
import { codegen } from '@graphql-codegen/core';
|
|
13
12
|
import * as typescriptPlugin from '@graphql-codegen/typescript';
|
|
14
13
|
import * as typescriptOperationsPlugin from '@graphql-codegen/typescript-operations';
|
|
15
14
|
import * as typedDocumentNodePlugin from '@graphql-codegen/typed-document-node';
|
|
15
|
+
import { resolveCacheConfig } from '../dist/runtime/app/lib/cache.js';
|
|
16
16
|
|
|
17
17
|
const buildCache = /* @__PURE__ */ new Map();
|
|
18
18
|
function getCachedLoader(baseKey, loader) {
|
|
@@ -118,9 +118,7 @@ ${localImports.join("\n")}
|
|
|
118
118
|
${remoteImports.join("\n")}
|
|
119
119
|
|
|
120
120
|
export const schema = stitchSchemas({
|
|
121
|
-
subschemas: [
|
|
122
|
-
${[mergedSchema, ...remoteSchemas].join(",\n ")}
|
|
123
|
-
],
|
|
121
|
+
subschemas: [${[mergedSchema, ...remoteSchemas].join(", ")}],
|
|
124
122
|
});
|
|
125
123
|
`.trim();
|
|
126
124
|
return { ts, ...splitModule(ts) };
|
|
@@ -134,24 +132,18 @@ async function loadLocalSchema({ importPath }) {
|
|
|
134
132
|
}
|
|
135
133
|
return module.schema;
|
|
136
134
|
}
|
|
137
|
-
function getSchemaSDL(schema) {
|
|
138
|
-
return printSchema(lexicographicSortSchema(schema));
|
|
139
|
-
}
|
|
140
|
-
function getDefaultSchema() {
|
|
141
|
-
return buildSchema(`type Query { _empty: String }`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
135
|
async function getRemoteSchemaTemplate({ endpoint, headers, hooks, loadSchema }) {
|
|
145
136
|
const hooksImports = hooks.map((hook, index) => `import hook${index} from ${JSON.stringify(hook.importPath)};`);
|
|
146
137
|
const hooksArray = hooks.map((_, index) => `hook${index}`);
|
|
147
138
|
const schema = await loadSchema();
|
|
148
139
|
const sdl = getSchemaSDL(schema);
|
|
149
140
|
const ts = `
|
|
141
|
+
import type { GraphQLSchema } from "graphql";
|
|
150
142
|
import { buildSchema } from "graphql";
|
|
151
|
-
import {
|
|
143
|
+
import { getRemoteExecutor } from "#graphql/runtime/remote-executor";
|
|
152
144
|
${hooksImports.join("\n")}
|
|
153
145
|
|
|
154
|
-
const executor =
|
|
146
|
+
const executor = getRemoteExecutor({
|
|
155
147
|
endpoint: "${endpoint}",
|
|
156
148
|
headers: ${JSON.stringify(headers)},
|
|
157
149
|
hooks: [${hooksArray.join(", ")}],
|
|
@@ -163,7 +155,7 @@ const sdl = \`${sdl.replace(/`/g, "\\`")}\`;
|
|
|
163
155
|
export const schema = {
|
|
164
156
|
schema: buildSchema(sdl),
|
|
165
157
|
executor,
|
|
166
|
-
}
|
|
158
|
+
} as unknown as GraphQLSchema;
|
|
167
159
|
`.trim();
|
|
168
160
|
return { ts, ...splitModule(ts) };
|
|
169
161
|
}
|
|
@@ -192,83 +184,26 @@ function stripSubscriptions(schema) {
|
|
|
192
184
|
directives: schema.getDirectives()
|
|
193
185
|
});
|
|
194
186
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return `
|
|
198
|
-
import type { GraphQLClient } from "graphql-request";
|
|
199
|
-
import type { Client as SSEClient } from "graphql-sse";
|
|
200
|
-
|
|
201
|
-
declare module "#app/nuxt" {
|
|
202
|
-
interface NuxtApp {
|
|
203
|
-
$getGraphQLClient: () => GraphQLClient;
|
|
204
|
-
$getGraphQLSSEClient: () => SSEClient;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
declare module "#app" {
|
|
209
|
-
interface NuxtApp {
|
|
210
|
-
$getGraphQLClient: () => GraphQLClient;
|
|
211
|
-
$getGraphQLSSEClient: () => SSEClient;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export {};
|
|
216
|
-
`.trim();
|
|
217
|
-
}
|
|
218
|
-
function getServerTypesTemplate() {
|
|
219
|
-
return `
|
|
220
|
-
import type { ExecutionRequest, ExecutionResult } from "@graphql-tools/utils";
|
|
221
|
-
|
|
222
|
-
declare module "h3" {
|
|
223
|
-
interface H3EventContext {
|
|
224
|
-
_graphqlInFlightRequestsMap?: Map<string, Promise<unknown>>;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
declare module "#graphql/runtime/remote-executor" {
|
|
229
|
-
export type CreateRemoteExecutorInput = {
|
|
230
|
-
endpoint: string;
|
|
231
|
-
headers: HeadersInput;
|
|
232
|
-
hooks: GraphQLRemoteExecHooks[];
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
export function createRemoteExecutor(options: {
|
|
236
|
-
endpoint: string;
|
|
237
|
-
headers: Record<string, string>;
|
|
238
|
-
hooks: GraphQLRemoteExecHooks[];
|
|
239
|
-
}): Executor;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export {};
|
|
243
|
-
`.trim();
|
|
187
|
+
function getSchemaSDL(schema) {
|
|
188
|
+
return printSchema(lexicographicSortSchema(schema));
|
|
244
189
|
}
|
|
245
|
-
function
|
|
246
|
-
return `
|
|
247
|
-
import type { DocumentNode } from "graphql";
|
|
248
|
-
|
|
249
|
-
declare global {
|
|
250
|
-
type GraphQLCacheConfig = {
|
|
251
|
-
policy: "no-cache" | "cache-first" | "network-first" | "swr";
|
|
252
|
-
ttl?: number;
|
|
253
|
-
keyPrefix: string;
|
|
254
|
-
keyVersion: string | number;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
export type HeadersInput = Record<string, string | null>;
|
|
190
|
+
function getDefaultSchema() {
|
|
191
|
+
return buildSchema(`type Query { _empty: String }`);
|
|
258
192
|
}
|
|
259
193
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
};
|
|
194
|
+
function addUniversalTemplate({ filename, getContents, emitTs }) {
|
|
195
|
+
let modulePath;
|
|
196
|
+
if (emitTs) {
|
|
197
|
+
modulePath = addTemplate({ filename: `${filename}.ts`, getContents: async () => (await getContents()).ts, write: true }).dst;
|
|
198
|
+
} else {
|
|
199
|
+
modulePath = addTemplate({ filename: `${filename}.mjs`, getContents: async () => (await getContents()).mjs, write: true }).dst;
|
|
200
|
+
addTemplate({ filename: `${filename}.d.ts`, getContents: async () => (await getContents()).dts });
|
|
266
201
|
}
|
|
202
|
+
addServerTemplate({ filename: `${filename}.mjs`, getContents: async () => (await getContents()).mjs });
|
|
203
|
+
return modulePath;
|
|
267
204
|
}
|
|
268
205
|
|
|
269
|
-
|
|
270
|
-
`.trim();
|
|
271
|
-
}
|
|
206
|
+
const version = "0.6.1";
|
|
272
207
|
|
|
273
208
|
async function getDocuments(documentsGlob) {
|
|
274
209
|
try {
|
|
@@ -406,12 +341,13 @@ function collectOperations(documents) {
|
|
|
406
341
|
|
|
407
342
|
const module$1 = defineNuxtModule({
|
|
408
343
|
meta: {
|
|
409
|
-
name: "nuxt-graphql",
|
|
344
|
+
name: "@lewebsimple/nuxt-graphql",
|
|
410
345
|
configKey: "graphql"
|
|
411
346
|
},
|
|
412
347
|
defaults: {},
|
|
413
348
|
async setup(options, nuxt) {
|
|
414
349
|
const logger = useLogger("@lewebsimple/nuxt-graphql");
|
|
350
|
+
logger.info(`@lewebsimple/nuxt-graphql v${version} loaded`);
|
|
415
351
|
const { resolve: resolveModule } = createResolver(import.meta.url);
|
|
416
352
|
const { rootDir } = nuxt.options;
|
|
417
353
|
const { resolve: resolveRoot, resolvePath: rawResolveRootPath } = createResolver(rootDir);
|
|
@@ -420,22 +356,15 @@ const module$1 = defineNuxtModule({
|
|
|
420
356
|
}
|
|
421
357
|
const nuxtAliases = {};
|
|
422
358
|
const nitroAliases = {};
|
|
359
|
+
const emitTs = Boolean(nuxt.options.dev) || Boolean(process.env.NUXT_MODULE_PREPARE);
|
|
360
|
+
nuxtAliases["#graphql/runtime/remote-executor"] = resolveModule("./runtime/server/lib/remote-executor");
|
|
361
|
+
nitroAliases["#graphql/runtime/remote-executor"] = resolveModule("./runtime/server/lib/remote-executor");
|
|
423
362
|
const contextInput = {
|
|
424
363
|
importPaths: await Promise.all((options.server?.context || []).map((path) => resolveRootPath(path)))
|
|
425
364
|
};
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
addServerTemplate({ filename: "graphql/context.mjs", getContents: () => getContextTemplate(contextInput).mjs });
|
|
430
|
-
} else {
|
|
431
|
-
contextDst = addTemplate({ filename: "graphql/context.mjs", getContents: () => getContextTemplate(contextInput).mjs, write: true }).dst;
|
|
432
|
-
addServerTemplate({ filename: "graphql/context.mjs", getContents: () => getContextTemplate(contextInput).mjs });
|
|
433
|
-
addTypeTemplate({ filename: "graphql/context.d.ts", getContents: () => getContextTemplate(contextInput).dts });
|
|
434
|
-
}
|
|
435
|
-
nuxtAliases["#graphql/context"] = contextDst;
|
|
436
|
-
nitroAliases["#graphql/context"] = contextDst;
|
|
437
|
-
nuxtAliases["#graphql/runtime/remote-executor"] = resolveModule("./runtime/server/lib/remote-executor");
|
|
438
|
-
nitroAliases["#graphql/runtime/remote-executor"] = resolveModule("./runtime/server/lib/remote-executor");
|
|
365
|
+
const contextPath = addUniversalTemplate({ filename: "graphql/context", getContents: () => getContextTemplate(contextInput), emitTs });
|
|
366
|
+
nuxtAliases["#graphql/context"] = contextPath;
|
|
367
|
+
nitroAliases["#graphql/context"] = contextPath;
|
|
439
368
|
const schemaInput = { local: {}, remote: {} };
|
|
440
369
|
const schemaLoaders = {};
|
|
441
370
|
for (const [schemaName, schemaDef] of Object.entries(options.server?.schema || {})) {
|
|
@@ -444,34 +373,24 @@ const module$1 = defineNuxtModule({
|
|
|
444
373
|
}
|
|
445
374
|
if (schemaDef.type === "local") {
|
|
446
375
|
const importPath = await resolveRootPath(schemaDef.path);
|
|
376
|
+
const loadSchema2 = getCachedLoader(`schema:local:${schemaName}`, async () => await loadLocalSchema({ importPath }));
|
|
447
377
|
schemaInput.local[schemaName] = { importPath };
|
|
448
|
-
schemaLoaders[schemaName] =
|
|
378
|
+
schemaLoaders[schemaName] = loadSchema2;
|
|
449
379
|
} else if (schemaDef.type === "remote") {
|
|
450
380
|
const { endpoint } = schemaDef;
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
loadSchema: getCachedLoader(`schema:remote:${schemaName}`, async () => await introspectRemoteSchema({ endpoint }))
|
|
456
|
-
};
|
|
457
|
-
if (nuxt.options.dev) {
|
|
458
|
-
addTemplate({ filename: `graphql/schemas/${schemaName}.ts`, getContents: async () => (await getRemoteSchemaTemplate(remoteSchemaInput)).ts, write: true });
|
|
459
|
-
addServerTemplate({ filename: `graphql/schemas/${schemaName}.ts`, getContents: async () => (await getRemoteSchemaTemplate(remoteSchemaInput)).ts });
|
|
460
|
-
} else {
|
|
461
|
-
addTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => (await getRemoteSchemaTemplate(remoteSchemaInput)).mjs, write: true });
|
|
462
|
-
addServerTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => (await getRemoteSchemaTemplate(remoteSchemaInput)).mjs });
|
|
463
|
-
addTypeTemplate({ filename: `graphql/schemas/${schemaName}.d.ts`, getContents: async () => (await getRemoteSchemaTemplate(remoteSchemaInput)).dts });
|
|
464
|
-
}
|
|
381
|
+
const loadSchema2 = getCachedLoader(`schema:remote:${schemaName}`, async () => await introspectRemoteSchema({ endpoint }));
|
|
382
|
+
const hooks = await Promise.all((schemaDef.hooks || []).map(async (hookPath) => ({ importPath: await resolveRootPath(hookPath) })));
|
|
383
|
+
const remoteSchemaInput = { endpoint, headers: schemaDef.headers || {}, hooks, loadSchema: loadSchema2 };
|
|
384
|
+
addUniversalTemplate({ filename: `graphql/schemas/${schemaName}`, getContents: () => getRemoteSchemaTemplate(remoteSchemaInput), emitTs });
|
|
465
385
|
schemaInput.remote[schemaName] = { importPath: `./schemas/${schemaName}` };
|
|
466
|
-
schemaLoaders[schemaName] =
|
|
386
|
+
schemaLoaders[schemaName] = loadSchema2;
|
|
467
387
|
} else {
|
|
468
388
|
throw new Error(`Unknown schema type for schema "${schemaName}"`);
|
|
469
389
|
}
|
|
470
390
|
}
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
nitroAliases["#graphql/schema"] = schemaDst;
|
|
391
|
+
const schemaPath = addUniversalTemplate({ filename: "graphql/schema", getContents: () => getSchemaTemplate(schemaInput), emitTs });
|
|
392
|
+
nuxtAliases["#graphql/schema"] = schemaPath;
|
|
393
|
+
nitroAliases["#graphql/schema"] = schemaPath;
|
|
475
394
|
const sdlPath = resolveRoot(options.saveSDL || "server/graphql/schema.graphql");
|
|
476
395
|
const loadSchema = getCachedLoader("schema:stitched", async () => {
|
|
477
396
|
const subschemas = await Promise.all(Object.values(schemaLoaders).map((loadSchema2) => loadSchema2()));
|
|
@@ -495,37 +414,14 @@ const module$1 = defineNuxtModule({
|
|
|
495
414
|
}
|
|
496
415
|
return documents;
|
|
497
416
|
});
|
|
498
|
-
const operationsInput = {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
addServerTemplate({ filename: "graphql/operations.ts", getContents: async () => (await getOperationsTemplate(operationsInput)).ts });
|
|
507
|
-
} else {
|
|
508
|
-
operationsDst = addTemplate({ filename: "graphql/operations.mjs", getContents: async () => (await getOperationsTemplate(operationsInput)).mjs, write: true }).dst;
|
|
509
|
-
addServerTemplate({ filename: "graphql/operations.mjs", getContents: async () => (await getOperationsTemplate(operationsInput)).mjs });
|
|
510
|
-
addTypeTemplate({ filename: "graphql/operations.d.ts", getContents: async () => (await getOperationsTemplate(operationsInput)).dts });
|
|
511
|
-
}
|
|
512
|
-
nuxtAliases["#graphql/operations"] = operationsDst;
|
|
513
|
-
nitroAliases["#graphql/operations"] = operationsDst;
|
|
514
|
-
const registryInput = {
|
|
515
|
-
loadDocuments,
|
|
516
|
-
documentGlob: options.client?.documents || "**/*.gql"
|
|
517
|
-
};
|
|
518
|
-
let registryDst;
|
|
519
|
-
if (nuxt.options.dev || process.env.NUXT_MODULE_PREPARE) {
|
|
520
|
-
registryDst = addTemplate({ filename: "graphql/registry.ts", getContents: async () => (await getRegistryTemplate(registryInput)).ts, write: true }).dst;
|
|
521
|
-
addServerTemplate({ filename: "graphql/registry.ts", getContents: async () => (await getRegistryTemplate(registryInput)).ts });
|
|
522
|
-
} else {
|
|
523
|
-
registryDst = addTemplate({ filename: "graphql/registry.mjs", getContents: async () => (await getRegistryTemplate(registryInput)).mjs, write: true }).dst;
|
|
524
|
-
addServerTemplate({ filename: "graphql/registry.mjs", getContents: async () => (await getRegistryTemplate(registryInput)).mjs });
|
|
525
|
-
addTypeTemplate({ filename: "graphql/registry.d.ts", getContents: async () => (await getRegistryTemplate(registryInput)).dts });
|
|
526
|
-
}
|
|
527
|
-
nuxtAliases["#graphql/registry"] = registryDst;
|
|
528
|
-
nitroAliases["#graphql/registry"] = registryDst;
|
|
417
|
+
const operationsInput = { loadSchema, loadDocuments, documentGlob: options.client?.documents || "**/*.gql" };
|
|
418
|
+
const operationsPath = addUniversalTemplate({ filename: "graphql/operations", getContents: () => getOperationsTemplate(operationsInput), emitTs });
|
|
419
|
+
nuxtAliases["#graphql/operations"] = operationsPath;
|
|
420
|
+
nitroAliases["#graphql/operations"] = operationsPath;
|
|
421
|
+
const registryInput = { loadDocuments, documentGlob: options.client?.documents || "**/*.gql" };
|
|
422
|
+
const registryPath = addUniversalTemplate({ filename: "graphql/registry", getContents: () => getRegistryTemplate(registryInput), emitTs });
|
|
423
|
+
nuxtAliases["#graphql/registry"] = registryPath;
|
|
424
|
+
nitroAliases["#graphql/registry"] = registryPath;
|
|
529
425
|
if (nuxt.options.dev) {
|
|
530
426
|
const configPath = resolveRoot(options.saveConfig || "graphql.config.json");
|
|
531
427
|
const config = {
|
|
@@ -540,9 +436,6 @@ const module$1 = defineNuxtModule({
|
|
|
540
436
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
541
437
|
nitroConfig.alias = defu(nitroConfig.alias, nitroAliases);
|
|
542
438
|
});
|
|
543
|
-
addTypeTemplate({ filename: "types/nuxt-graphql.app.d.ts", getContents: () => getAppTypesTemplate() }, { nuxt: true });
|
|
544
|
-
addTypeTemplate({ filename: "types/nuxt-graphql.server.d.ts", getContents: () => getServerTypesTemplate() }, { nitro: true, node: true });
|
|
545
|
-
addTypeTemplate({ filename: "types/nuxt-graphql.shared.d.ts", getContents: () => getSharedTypesTemplate() }, { nuxt: true, nitro: true, node: true });
|
|
546
439
|
nuxt.options.runtimeConfig.public.graphql = defu(nuxt.options.runtimeConfig.public.graphql, {
|
|
547
440
|
cacheConfig: resolveCacheConfig(options.client?.cache),
|
|
548
441
|
ssrForwardHeaders: options.client?.ssrForwardHeaders || ["authorization", "cookie"]
|
|
@@ -560,9 +453,10 @@ const module$1 = defineNuxtModule({
|
|
|
560
453
|
nuxt.hook("listen", (_, { url }) => {
|
|
561
454
|
logger.success(`GraphQL Yoga ready: ${cyan}${url.replace(/\/$/, "")}/api/graphql${reset}`);
|
|
562
455
|
});
|
|
563
|
-
addPlugin(resolveModule("./runtime/app/plugins/graphql
|
|
456
|
+
addPlugin(resolveModule("./runtime/app/plugins/execute-graphql"));
|
|
564
457
|
addPlugin(resolveModule("./runtime/app/plugins/graphql-sse.client"));
|
|
565
458
|
addImportsDir(resolveModule("./runtime/app/composables"));
|
|
459
|
+
addImportsDir(resolveModule("./runtime/shared/utils"));
|
|
566
460
|
addServerImportsDir(resolveModule("./runtime/server/utils"));
|
|
567
461
|
}
|
|
568
462
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type AsyncData, type AsyncDataOptions } from "#app";
|
|
2
2
|
import type { QueryName, ResultOf, VariablesOf } from "#graphql/registry";
|
|
3
3
|
import { type MaybeRefOrGetter } from "#imports";
|
|
4
|
-
import {
|
|
5
|
-
import type
|
|
6
|
-
type UseAsyncGraphQLQueryOptions<TName extends QueryName> =
|
|
7
|
-
cache?: Partial<
|
|
8
|
-
}
|
|
4
|
+
import type { IsEmptyObject } from "../../shared/lib/types.js";
|
|
5
|
+
import { type CacheConfig } from "../lib/cache.js";
|
|
6
|
+
type UseAsyncGraphQLQueryOptions<TName extends QueryName> = AsyncDataOptions<ResultOf<TName>> & {
|
|
7
|
+
cache?: Partial<CacheConfig>;
|
|
8
|
+
};
|
|
9
9
|
/**
|
|
10
10
|
* Async GraphQL query composable with caching support.
|
|
11
11
|
*
|
|
@@ -13,5 +13,5 @@ type UseAsyncGraphQLQueryOptions<TName extends QueryName> = ExecuteGraphQLHTTPOp
|
|
|
13
13
|
* @param args Operation variables (if any) and optional HTTP headers.
|
|
14
14
|
* @returns Nuxt AsyncData wrapper for the query result.
|
|
15
15
|
*/
|
|
16
|
-
export declare function useAsyncGraphQLQuery<TName extends QueryName>(operationName: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: MaybeRefOrGetter<VariablesOf<TName>>, options?: UseAsyncGraphQLQueryOptions<TName>] : [variables: MaybeRefOrGetter<VariablesOf<TName>>, options?: UseAsyncGraphQLQueryOptions<TName>]):
|
|
16
|
+
export declare function useAsyncGraphQLQuery<TName extends QueryName>(operationName: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: MaybeRefOrGetter<VariablesOf<TName>>, options?: UseAsyncGraphQLQueryOptions<TName>] : [variables: MaybeRefOrGetter<VariablesOf<TName>>, options?: UseAsyncGraphQLQueryOptions<TName>]): AsyncData<ResultOf<TName> | null, Error | undefined>;
|
|
17
17
|
export {};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { useAsyncData, useNuxtData, useRuntimeConfig } from "#app";
|
|
1
|
+
import { useAsyncData, useNuxtApp, useNuxtData, useRuntimeConfig } from "#app";
|
|
2
2
|
import { computed, toValue } from "#imports";
|
|
3
|
-
import {
|
|
3
|
+
import { getOperationDocument } from "../../shared/lib/registry.js";
|
|
4
|
+
import { getCacheKeyParts, resolveCacheConfig } from "../lib/cache.js";
|
|
4
5
|
import { getInFlightRequests } from "../lib/in-flight.js";
|
|
5
6
|
import { getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
|
|
6
|
-
import { getCacheKeyParts, resolveCacheConfig } from "../../shared/lib/cache.js";
|
|
7
7
|
export function useAsyncGraphQLQuery(operationName, ...args) {
|
|
8
|
+
const { $executeGraphQL } = useNuxtApp();
|
|
8
9
|
const [variables, options] = args;
|
|
10
|
+
const document = getOperationDocument(operationName);
|
|
9
11
|
const isClient = import.meta.client;
|
|
10
12
|
const { public: { graphql } } = useRuntimeConfig();
|
|
11
|
-
const {
|
|
13
|
+
const { cache, ...asyncDataOptions } = options ?? {};
|
|
12
14
|
const cacheConfig = resolveCacheConfig(graphql.cacheConfig, cache);
|
|
13
15
|
const cacheKey = computed(() => getCacheKeyParts(cacheConfig, operationName, toValue(variables)).key);
|
|
14
16
|
const inFlight = getInFlightRequests();
|
|
@@ -17,16 +19,18 @@ export function useAsyncGraphQLQuery(operationName, ...args) {
|
|
|
17
19
|
if (inFlight.has(key)) {
|
|
18
20
|
return inFlight.get(key);
|
|
19
21
|
}
|
|
20
|
-
const promise =
|
|
22
|
+
const promise = $executeGraphQL({ query: document, variables: toValue(variables), operationName }).then((result) => {
|
|
23
|
+
if (result.error) {
|
|
24
|
+
throw result.error;
|
|
25
|
+
}
|
|
26
|
+
const data = result.data;
|
|
21
27
|
if (isClient && cacheConfig.ttl !== void 0) {
|
|
22
28
|
setPersistedEntry(key, data, cacheConfig.ttl);
|
|
23
29
|
}
|
|
24
30
|
return data;
|
|
25
31
|
});
|
|
26
32
|
inFlight.set(key, promise);
|
|
27
|
-
promise.finally(() =>
|
|
28
|
-
inFlight.delete(key);
|
|
29
|
-
});
|
|
33
|
+
promise.finally(() => inFlight.delete(key));
|
|
30
34
|
return promise;
|
|
31
35
|
}
|
|
32
36
|
async function asyncDataHandler() {
|