@lewebsimple/nuxt-graphql 0.1.8 → 0.1.10
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/dist/module.d.mts +0 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +73 -65
- package/dist/runtime/composables/useGraphQLMutation.d.ts +6 -7
- package/dist/runtime/composables/useGraphQLMutation.js +8 -11
- package/dist/runtime/composables/useGraphQLQuery.d.ts +2 -3
- package/dist/runtime/composables/useGraphQLQuery.js +5 -6
- package/dist/runtime/composables/useGraphQLSubscription.d.ts +10 -0
- package/dist/runtime/composables/useGraphQLSubscription.js +45 -0
- package/dist/runtime/plugins/graphql.d.ts +5 -2
- package/dist/runtime/plugins/graphql.js +27 -4
- package/dist/runtime/server/utils/graphql-client.d.ts +15 -0
- package/dist/runtime/server/utils/graphql-client.js +14 -0
- package/dist/runtime/server/utils/useGraphQLMutation.d.ts +9 -0
- package/dist/runtime/server/utils/useGraphQLMutation.js +16 -0
- package/dist/runtime/server/utils/useGraphQLQuery.d.ts +4 -0
- package/dist/runtime/server/utils/useGraphQLQuery.js +7 -0
- package/dist/runtime/types/graphql-client.d.ts +3 -1
- package/dist/runtime/utils/helpers.d.ts +1 -0
- package/dist/runtime/utils/helpers.js +0 -0
- package/dist/templates/yoga-handler.mjs +16 -5
- package/package.json +5 -4
- package/dist/runtime/composables/useGraphQL.d.ts +0 -4
- package/dist/runtime/composables/useGraphQL.js +0 -8
- /package/dist/runtime/server/{default-context.d.ts → graphql/default-context.d.ts} +0 -0
- /package/dist/runtime/server/{default-context.js → graphql/default-context.js} +0 -0
package/dist/module.d.mts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useLogger, defineNuxtModule, createResolver, getLayerDirectories, addServerTemplate, addServerHandler, addImportsDir, addPlugin, addTypeTemplate } from '@nuxt/kit';
|
|
1
|
+
import { useLogger, defineNuxtModule, createResolver, getLayerDirectories, addServerTemplate, addServerHandler, addImportsDir, addServerImportsDir, addPlugin, addTypeTemplate } from '@nuxt/kit';
|
|
2
2
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join, dirname, relative } from 'node:path';
|
|
4
4
|
import { glob } from 'tinyglobby';
|
|
@@ -44,6 +44,16 @@ function writeFileIfChanged(path, content) {
|
|
|
44
44
|
return true;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
async function loadGraphQLSchema(schemaPath) {
|
|
48
|
+
const { createJiti } = await import('jiti');
|
|
49
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
50
|
+
const module = await jiti.import(schemaPath);
|
|
51
|
+
if (!module.schema) {
|
|
52
|
+
throw new Error(`${schemaPath} must export a 'schema' variable`);
|
|
53
|
+
}
|
|
54
|
+
const { printSchema, lexicographicSortSchema } = await import('graphql');
|
|
55
|
+
return printSchema(lexicographicSortSchema(module.schema));
|
|
56
|
+
}
|
|
47
57
|
function analyzeGraphQLDocuments(docs) {
|
|
48
58
|
const byFile = /* @__PURE__ */ new Map();
|
|
49
59
|
const operationsByType = {
|
|
@@ -92,9 +102,9 @@ function analyzeGraphQLDocuments(docs) {
|
|
|
92
102
|
return { byFile, operationsByType };
|
|
93
103
|
}
|
|
94
104
|
function generateRegistryByTypeSource(analysis) {
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
105
|
+
const queries = analysis.query.map((o) => o.name);
|
|
106
|
+
const mutations = analysis.mutation.map((o) => o.name);
|
|
107
|
+
const subscriptions = analysis.subscription.map((o) => o.name);
|
|
98
108
|
const lines = [
|
|
99
109
|
`import type { TypedDocumentNode } from "@graphql-typed-document-node/core";`,
|
|
100
110
|
`import * as ops from "#graphql/operations";`,
|
|
@@ -102,11 +112,11 @@ function generateRegistryByTypeSource(analysis) {
|
|
|
102
112
|
`type ResultOf<T> = T extends { __apiType?: (variables: infer _) => infer R } ? R : never;`,
|
|
103
113
|
`type VariablesOf<T> = T extends { __apiType?: (variables: infer V) => infer _ } ? V : never;`
|
|
104
114
|
];
|
|
105
|
-
if (
|
|
115
|
+
if (queries.length > 0) {
|
|
106
116
|
lines.push(
|
|
107
117
|
``,
|
|
108
118
|
`export const queries = {`,
|
|
109
|
-
...
|
|
119
|
+
...queries.map((name) => ` ${name}: ops.${name}Document,`),
|
|
110
120
|
`} as const;`
|
|
111
121
|
);
|
|
112
122
|
} else {
|
|
@@ -117,11 +127,11 @@ function generateRegistryByTypeSource(analysis) {
|
|
|
117
127
|
`export type QueryResult<N extends QueryName> = ResultOf<(typeof queries)[N]>;`,
|
|
118
128
|
`export type QueryVariables<N extends QueryName> = VariablesOf<(typeof queries)[N]>;`
|
|
119
129
|
);
|
|
120
|
-
if (
|
|
130
|
+
if (mutations.length > 0) {
|
|
121
131
|
lines.push(
|
|
122
132
|
``,
|
|
123
133
|
`export const mutations = {`,
|
|
124
|
-
...
|
|
134
|
+
...mutations.map((name) => ` ${name}: ops.${name}Document,`),
|
|
125
135
|
`} as const;`
|
|
126
136
|
);
|
|
127
137
|
} else {
|
|
@@ -132,11 +142,11 @@ function generateRegistryByTypeSource(analysis) {
|
|
|
132
142
|
`export type MutationResult<N extends MutationName> = ResultOf<(typeof mutations)[N]>;`,
|
|
133
143
|
`export type MutationVariables<N extends MutationName> = VariablesOf<(typeof mutations)[N]>;`
|
|
134
144
|
);
|
|
135
|
-
if (
|
|
145
|
+
if (subscriptions.length > 0) {
|
|
136
146
|
lines.push(
|
|
137
147
|
``,
|
|
138
148
|
`export const subscriptions = {`,
|
|
139
|
-
...
|
|
149
|
+
...subscriptions.map((name) => ` ${name}: ops.${name}Document,`),
|
|
140
150
|
`} as const;`
|
|
141
151
|
);
|
|
142
152
|
} else {
|
|
@@ -164,16 +174,6 @@ function formatDefinitions(defs) {
|
|
|
164
174
|
};
|
|
165
175
|
return defs.map((def) => `${colorOf(def)}${def.name}${reset}`).join(`${dim} / ${reset}`);
|
|
166
176
|
}
|
|
167
|
-
async function loadGraphQLSchema(schemaPath) {
|
|
168
|
-
const { createJiti } = await import('jiti');
|
|
169
|
-
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
170
|
-
const module = await jiti.import(schemaPath);
|
|
171
|
-
if (!module.schema) {
|
|
172
|
-
throw new Error(`${schemaPath} must export a 'schema' variable`);
|
|
173
|
-
}
|
|
174
|
-
const { printSchema, lexicographicSortSchema } = await import('graphql');
|
|
175
|
-
return printSchema(lexicographicSortSchema(module.schema));
|
|
176
|
-
}
|
|
177
177
|
async function runCodegen(options) {
|
|
178
178
|
const { sdl, documents, operationsFile } = options;
|
|
179
179
|
if (documents.length === 0) {
|
|
@@ -219,13 +219,17 @@ const module$1 = defineNuxtModule({
|
|
|
219
219
|
defaults: {
|
|
220
220
|
endpoint: "/api/graphql",
|
|
221
221
|
codegen: {
|
|
222
|
-
enabled: true,
|
|
223
222
|
pattern: "**/*.gql",
|
|
224
223
|
schemaOutput: "server/graphql/schema.graphql"
|
|
225
224
|
}
|
|
226
225
|
},
|
|
227
226
|
async setup(options, nuxt) {
|
|
228
227
|
const { resolve } = createResolver(import.meta.url);
|
|
228
|
+
if (options.endpoint) {
|
|
229
|
+
if (!options.endpoint.startsWith("/")) {
|
|
230
|
+
logger.warn("GraphQL endpoint should start with '/' (e.g., '/api/graphql')");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
229
233
|
const { rootDir, serverDir } = nuxt.options;
|
|
230
234
|
const layerDirs = [
|
|
231
235
|
...getLayerDirectories(nuxt),
|
|
@@ -234,7 +238,7 @@ const module$1 = defineNuxtModule({
|
|
|
234
238
|
const layerServerDirs = layerDirs.map(({ server }) => server);
|
|
235
239
|
const layerRootDirs = layerDirs.map(({ root }) => root);
|
|
236
240
|
const schemaPath = await findSingleFile(layerServerDirs, "graphql/schema.{ts,mjs}", true);
|
|
237
|
-
const contextPath = await findSingleFile(layerServerDirs, "graphql/context.{ts,mjs}") || resolve("./runtime/server/default-context.ts");
|
|
241
|
+
const contextPath = await findSingleFile(layerServerDirs, "graphql/context.{ts,mjs}") || resolve("./runtime/server/graphql/default-context.ts");
|
|
238
242
|
nuxt.hook("nitro:config", (config) => {
|
|
239
243
|
config.alias ||= {};
|
|
240
244
|
config.alias["#graphql/schema"] = schemaPath;
|
|
@@ -250,53 +254,57 @@ const module$1 = defineNuxtModule({
|
|
|
250
254
|
logger.success(`GraphQL Yoga ready at ${cyan}${url.replace(/\/$/, "")}${endpoint}${reset}`);
|
|
251
255
|
});
|
|
252
256
|
nuxt.options.runtimeConfig.public.graphql = { endpoint };
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
257
|
+
const codegenPattern = options.codegen?.pattern ?? "**/*.gql";
|
|
258
|
+
const graphqlrcFile = join(rootDir, ".graphqlrc");
|
|
259
|
+
const operationsFile = join(nuxt.options.buildDir, "graphql/operations.ts");
|
|
260
|
+
const registryFile = join(nuxt.options.buildDir, "graphql/registry.ts");
|
|
261
|
+
nuxt.options.alias["#graphql/operations"] = operationsFile;
|
|
262
|
+
nuxt.options.alias["#graphql/registry"] = registryFile;
|
|
263
|
+
const schemaOutput = options.codegen?.schemaOutput ?? "server/graphql/schema.graphql";
|
|
264
|
+
if (schemaOutput) {
|
|
265
|
+
if (!schemaOutput.endsWith(".graphql")) {
|
|
266
|
+
logger.warn(`Schema output '${schemaOutput}' should have .graphql extension.`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const schemaFile = join(rootDir, schemaOutput);
|
|
270
|
+
const generate = async () => {
|
|
271
|
+
const [sdl, documents] = await Promise.all([
|
|
272
|
+
loadGraphQLSchema(schemaPath),
|
|
273
|
+
findMultipleFiles(layerRootDirs, codegenPattern)
|
|
274
|
+
]);
|
|
275
|
+
const docs = documents.map((document) => ({ path: document, content: readFileSync(document, "utf-8") }));
|
|
276
|
+
const analysis = analyzeGraphQLDocuments(docs);
|
|
277
|
+
for (const doc of docs) {
|
|
278
|
+
const relativePath = doc.path.startsWith(rootDir) ? doc.path.slice(rootDir.length + 1) : doc.path;
|
|
279
|
+
const defs = analysis.byFile.get(doc.path) ?? [];
|
|
280
|
+
logger.info(`${cyan}${relativePath}${reset} [${formatDefinitions(defs)}]`);
|
|
281
|
+
}
|
|
282
|
+
await runCodegen({ sdl, documents, operationsFile });
|
|
283
|
+
if (writeFileIfChanged(schemaFile, sdl)) {
|
|
284
|
+
logger.info(`GraphQL schema saved to ${cyan}${schemaOutput}${reset}`);
|
|
285
|
+
}
|
|
286
|
+
const config = JSON.stringify({ schema: relative(rootDir, schemaFile), documents: codegenPattern }, null, 2);
|
|
287
|
+
if (writeFileIfChanged(graphqlrcFile, config)) {
|
|
288
|
+
logger.info(`GraphQL config saved to ${cyan}.graphqlrc${reset}`);
|
|
289
|
+
}
|
|
290
|
+
if (writeFileIfChanged(registryFile, generateRegistryByTypeSource(analysis.operationsByType))) {
|
|
291
|
+
logger.info(`GraphQL registry saved to ${cyan}${relative(rootDir, registryFile)}${reset}`);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
nuxt.hook("prepare:types", async ({ references }) => {
|
|
295
|
+
await generate();
|
|
296
|
+
if (existsSync(operationsFile)) references.push({ path: operationsFile });
|
|
297
|
+
if (existsSync(registryFile)) references.push({ path: registryFile });
|
|
298
|
+
});
|
|
299
|
+
if (nuxt.options.dev) {
|
|
300
|
+
nuxt.hook("builder:watch", async (event, path) => {
|
|
301
|
+
if (path.endsWith(".gql")) {
|
|
302
|
+
await generate();
|
|
282
303
|
}
|
|
283
|
-
};
|
|
284
|
-
nuxt.hook("prepare:types", async ({ references }) => {
|
|
285
|
-
await generate();
|
|
286
|
-
if (existsSync(operationsFile)) references.push({ path: operationsFile });
|
|
287
|
-
if (existsSync(registryFile)) references.push({ path: registryFile });
|
|
288
304
|
});
|
|
289
|
-
if (nuxt.options.dev) {
|
|
290
|
-
nuxt.hook("builder:watch", async (event, path) => {
|
|
291
|
-
if (path.endsWith(".gql")) {
|
|
292
|
-
await generate();
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
nuxt.options.alias["#graphql/operations"] = operationsFile;
|
|
297
|
-
nuxt.options.alias["#graphql/registry"] = registryFile;
|
|
298
305
|
}
|
|
299
306
|
addImportsDir(resolve("./runtime/composables"));
|
|
307
|
+
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
300
308
|
addPlugin(resolve("./runtime/plugins/graphql"));
|
|
301
309
|
addTypeTemplate({
|
|
302
310
|
filename: "types/graphql-client.d.ts",
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { type Ref } from "vue";
|
|
2
1
|
import { type MutationName, type MutationResult, type MutationVariables } from "#graphql/registry";
|
|
3
|
-
type IsEmptyObject
|
|
2
|
+
import type { IsEmptyObject } from "../utils/helpers.js";
|
|
4
3
|
export declare function useGraphQLMutation<N extends MutationName>(operationName: N): {
|
|
5
|
-
mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N>, headers?: HeadersInit] : [variables: MutationVariables<N>, headers?: HeadersInit]) => Promise<{
|
|
5
|
+
data: MutationResult<N> | null;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
}>;
|
|
8
|
+
pending: import("vue").Ref<boolean, boolean>;
|
|
9
9
|
};
|
|
10
|
-
export {};
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { ref } from "vue";
|
|
2
|
-
import {
|
|
2
|
+
import { useNuxtApp } from "#imports";
|
|
3
3
|
import { mutations } from "#graphql/registry";
|
|
4
4
|
export function useGraphQLMutation(operationName) {
|
|
5
5
|
const document = mutations[operationName];
|
|
6
|
-
const {
|
|
7
|
-
const data = ref(null);
|
|
8
|
-
const error = ref(null);
|
|
6
|
+
const { $graphql } = useNuxtApp();
|
|
9
7
|
const pending = ref(false);
|
|
10
8
|
async function mutate(...args) {
|
|
11
9
|
pending.value = true;
|
|
12
|
-
error.value = null;
|
|
13
10
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
return result;
|
|
11
|
+
const [variables, headers] = args;
|
|
12
|
+
const result = await $graphql().request(document, variables, headers);
|
|
13
|
+
return { data: result, error: null };
|
|
17
14
|
} catch (e) {
|
|
18
|
-
error
|
|
19
|
-
|
|
15
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
16
|
+
return { data: null, error };
|
|
20
17
|
} finally {
|
|
21
18
|
pending.value = false;
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
|
-
return { mutate,
|
|
21
|
+
return { mutate, pending };
|
|
25
22
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AsyncData, AsyncDataOptions } from "#app";
|
|
2
2
|
import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
|
|
3
|
-
type IsEmptyObject
|
|
4
|
-
export declare function useGraphQLQuery<N extends QueryName>(operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>,
|
|
5
|
-
export {};
|
|
3
|
+
import type { IsEmptyObject } from "../utils/helpers.js";
|
|
4
|
+
export declare function useGraphQLQuery<N extends QueryName>(operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, options?: AsyncDataOptions<QueryResult<N>>, headers?: HeadersInit] : [variables: QueryVariables<N>, options?: AsyncDataOptions<QueryResult<N>>, headers?: HeadersInit]): AsyncData<QueryResult<N>, Error | null>;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { useAsyncData } from "#imports";
|
|
1
|
+
import { useAsyncData, useNuxtApp } from "#imports";
|
|
2
2
|
import { hash } from "ohash";
|
|
3
|
-
import { useGraphQL } from "./useGraphQL.js";
|
|
4
3
|
import { queries } from "#graphql/registry";
|
|
5
4
|
export function useGraphQLQuery(operationName, ...args) {
|
|
5
|
+
const { $graphql } = useNuxtApp();
|
|
6
6
|
const document = queries[operationName];
|
|
7
|
-
const [variables,
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
return useAsyncData(key, () => request(document, variables), opts);
|
|
7
|
+
const [variables, options, headers] = args;
|
|
8
|
+
const key = `graphql:query:${operationName}:${hash(variables ?? {})}`;
|
|
9
|
+
return useAsyncData(key, () => $graphql().request(document, variables, headers), options);
|
|
11
10
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter, type Ref } from "vue";
|
|
2
|
+
import { type SubscriptionName, type SubscriptionResult, type SubscriptionVariables } from "#graphql/registry";
|
|
3
|
+
import type { IsEmptyObject } from "../utils/helpers.js";
|
|
4
|
+
export type UseGraphQLSubscriptionReturn<N extends SubscriptionName> = {
|
|
5
|
+
data: Ref<SubscriptionResult<N> | null>;
|
|
6
|
+
error: Ref<Error | null>;
|
|
7
|
+
start: () => void;
|
|
8
|
+
stop: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function useGraphQLSubscription<N extends SubscriptionName>(operationName: N, ...args: IsEmptyObject<SubscriptionVariables<N>> extends true ? [variables?: MaybeRefOrGetter<SubscriptionVariables<N>>] : [variables: MaybeRefOrGetter<SubscriptionVariables<N>>]): UseGraphQLSubscriptionReturn<N>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ref, onScopeDispose, toValue } from "vue";
|
|
2
|
+
import { print } from "graphql";
|
|
3
|
+
import { useNuxtApp } from "#imports";
|
|
4
|
+
import { subscriptions } from "#graphql/registry";
|
|
5
|
+
export function useGraphQLSubscription(operationName, ...args) {
|
|
6
|
+
const { $graphqlSSE } = useNuxtApp();
|
|
7
|
+
const [variables] = args;
|
|
8
|
+
const data = ref(null);
|
|
9
|
+
const error = ref(null);
|
|
10
|
+
let unsubscribe = null;
|
|
11
|
+
function start() {
|
|
12
|
+
stop();
|
|
13
|
+
error.value = null;
|
|
14
|
+
unsubscribe = $graphqlSSE().subscribe(
|
|
15
|
+
{
|
|
16
|
+
query: print(subscriptions[operationName]),
|
|
17
|
+
variables: toValue(variables)
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
next: (result) => {
|
|
21
|
+
if (result.errors?.length) {
|
|
22
|
+
error.value = new Error(result.errors.map((e) => e.message).join(", "));
|
|
23
|
+
} else if (result.data) {
|
|
24
|
+
data.value = result.data;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
error: (e) => {
|
|
28
|
+
error.value = e instanceof Error ? e : new Error(String(e));
|
|
29
|
+
},
|
|
30
|
+
complete: () => {
|
|
31
|
+
unsubscribe = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function stop() {
|
|
37
|
+
unsubscribe?.();
|
|
38
|
+
unsubscribe = null;
|
|
39
|
+
}
|
|
40
|
+
if (import.meta.client) {
|
|
41
|
+
start();
|
|
42
|
+
}
|
|
43
|
+
onScopeDispose(stop);
|
|
44
|
+
return { data, error, start, stop };
|
|
45
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { GraphQLClient } from "graphql-request";
|
|
2
|
+
import { type Client as SSEClient } from "graphql-sse";
|
|
2
3
|
declare const _default: import("nuxt/app").Plugin<{
|
|
3
|
-
graphql: GraphQLClient;
|
|
4
|
+
graphql: () => GraphQLClient;
|
|
5
|
+
graphqlSSE: () => SSEClient;
|
|
4
6
|
}> & import("nuxt/app").ObjectPlugin<{
|
|
5
|
-
graphql: GraphQLClient;
|
|
7
|
+
graphql: () => GraphQLClient;
|
|
8
|
+
graphqlSSE: () => SSEClient;
|
|
6
9
|
}>;
|
|
7
10
|
export default _default;
|
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import { GraphQLClient } from "graphql-request";
|
|
2
|
-
import {
|
|
2
|
+
import { createClient } from "graphql-sse";
|
|
3
|
+
import { defineNuxtPlugin, useRequestHeaders, useRequestURL, useRuntimeConfig } from "#imports";
|
|
3
4
|
export default defineNuxtPlugin(() => {
|
|
4
|
-
const
|
|
5
|
+
const { public: { graphql: { endpoint } } } = useRuntimeConfig();
|
|
5
6
|
const { origin } = useRequestURL();
|
|
6
|
-
const
|
|
7
|
+
const url = `${origin}${endpoint}`;
|
|
8
|
+
let client = null;
|
|
9
|
+
const getClient = () => {
|
|
10
|
+
if (!client) {
|
|
11
|
+
client = new GraphQLClient(url);
|
|
12
|
+
}
|
|
13
|
+
if (import.meta.server) {
|
|
14
|
+
const headers = useRequestHeaders(["cookie", "authorization"]);
|
|
15
|
+
client.setHeaders(headers);
|
|
16
|
+
}
|
|
17
|
+
return client;
|
|
18
|
+
};
|
|
19
|
+
let sseClient = null;
|
|
20
|
+
const getSSEClient = () => {
|
|
21
|
+
if (import.meta.server) {
|
|
22
|
+
throw new Error("SSE subscriptions are not available on the server");
|
|
23
|
+
}
|
|
24
|
+
if (!sseClient) {
|
|
25
|
+
sseClient = createClient({ url });
|
|
26
|
+
}
|
|
27
|
+
return sseClient;
|
|
28
|
+
};
|
|
7
29
|
return {
|
|
8
30
|
provide: {
|
|
9
|
-
graphql:
|
|
31
|
+
graphql: getClient,
|
|
32
|
+
graphqlSSE: getSSEClient
|
|
10
33
|
}
|
|
11
34
|
};
|
|
12
35
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
import { GraphQLClient } from "graphql-request";
|
|
3
|
+
/**
|
|
4
|
+
* Create server-side GraphQL client instance for the given H3 event.
|
|
5
|
+
*
|
|
6
|
+
* @param event H3 event
|
|
7
|
+
*
|
|
8
|
+
* @returns GraphQL client instance
|
|
9
|
+
*/
|
|
10
|
+
export declare function getGraphQLClient(event: H3Event): GraphQLClient;
|
|
11
|
+
declare module "h3" {
|
|
12
|
+
interface H3EventContext {
|
|
13
|
+
__nuxtGraphQLClient?: GraphQLClient;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getRequestHeaders, getRequestURL } from "h3";
|
|
2
|
+
import { GraphQLClient } from "graphql-request";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
export function getGraphQLClient(event) {
|
|
5
|
+
if (event.context.__nuxtGraphQLClient) {
|
|
6
|
+
return event.context.__nuxtGraphQLClient;
|
|
7
|
+
}
|
|
8
|
+
const { public: { graphql: { endpoint } } } = useRuntimeConfig();
|
|
9
|
+
const { origin } = getRequestURL(event);
|
|
10
|
+
const url = `${origin}${endpoint}`;
|
|
11
|
+
const headers = getRequestHeaders(event);
|
|
12
|
+
event.context.__nuxtGraphQLClient = new GraphQLClient(url, { headers });
|
|
13
|
+
return event.context.__nuxtGraphQLClient;
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
import { type MutationName, type MutationResult, type MutationVariables } from "#graphql/registry";
|
|
3
|
+
import type { IsEmptyObject } from "../../utils/helpers.js";
|
|
4
|
+
export declare function useGraphQLMutation<N extends MutationName>(event: H3Event, operationName: N): Promise<{
|
|
5
|
+
mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N>, headers?: HeadersInit] : [variables: MutationVariables<N>, headers?: HeadersInit]) => Promise<{
|
|
6
|
+
data: MutationResult<N> | null;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getGraphQLClient } from "./graphql-client.js";
|
|
2
|
+
import { mutations } from "#graphql/registry";
|
|
3
|
+
export async function useGraphQLMutation(event, operationName) {
|
|
4
|
+
const client = getGraphQLClient(event);
|
|
5
|
+
async function mutate(...args) {
|
|
6
|
+
try {
|
|
7
|
+
const [variables, headers] = args;
|
|
8
|
+
const result = await client.request(mutations[operationName], variables, headers);
|
|
9
|
+
return { data: result, error: null };
|
|
10
|
+
} catch (e) {
|
|
11
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
12
|
+
return { data: null, error };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return { mutate };
|
|
16
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
|
|
3
|
+
import type { IsEmptyObject } from "../../utils/helpers.js";
|
|
4
|
+
export declare function useGraphQLQuery<N extends QueryName>(event: H3Event, operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, headers?: HeadersInit] : [variables: QueryVariables<N>, headers?: HeadersInit]): Promise<QueryResult<N>>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getGraphQLClient } from "./graphql-client.js";
|
|
2
|
+
import { queries } from "#graphql/registry";
|
|
3
|
+
export async function useGraphQLQuery(event, operationName, ...args) {
|
|
4
|
+
const client = getGraphQLClient(event);
|
|
5
|
+
const [variables, headers] = args;
|
|
6
|
+
return client.request(queries[operationName], variables, headers);
|
|
7
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { GraphQLClient } from "graphql-request";
|
|
2
|
+
import type { Client as SSEClient } from "graphql-sse";
|
|
2
3
|
|
|
3
4
|
declare module "#app" {
|
|
4
5
|
interface NuxtApp {
|
|
5
|
-
$graphql: GraphQLClient;
|
|
6
|
+
$graphql: () => GraphQLClient;
|
|
7
|
+
$graphqlSSE: () => SSEClient;
|
|
6
8
|
}
|
|
7
9
|
}
|
|
8
10
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type IsEmptyObject<T> = T extends Record<string, never> ? true : keyof T extends never ? true : false;
|
|
File without changes
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createYoga } from "graphql-yoga";
|
|
2
|
-
import { defineEventHandler, toWebRequest, sendWebResponse } from "h3";
|
|
2
|
+
import { defineEventHandler, toWebRequest, sendWebResponse, createError } from "h3";
|
|
3
3
|
import { schema } from "#graphql/schema";
|
|
4
4
|
import { createContext } from "#graphql/context";
|
|
5
|
+
import { useLogger } from "@nuxt/kit";
|
|
5
6
|
|
|
6
7
|
let yoga = null;
|
|
7
8
|
|
|
@@ -11,14 +12,24 @@ function getYoga() {
|
|
|
11
12
|
schema,
|
|
12
13
|
graphqlEndpoint: "{{endpoint}}",
|
|
13
14
|
fetchAPI: globalThis,
|
|
15
|
+
graphiql: process.env.NODE_ENV !== "production",
|
|
16
|
+
subscriptions: { protocol: "SSE" },
|
|
14
17
|
});
|
|
15
18
|
}
|
|
16
19
|
return yoga;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export default defineEventHandler(async (event) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const logger = useLogger();
|
|
24
|
+
try {
|
|
25
|
+
const request = toWebRequest(event);
|
|
26
|
+
const context = await createContext(event);
|
|
27
|
+
const response = await getYoga().handleRequest(request, context);
|
|
28
|
+
return sendWebResponse(event, response);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
+
logger.error("GraphQL Server Error:", message);
|
|
33
|
+
throw createError({ statusCode: 500, message: "GraphQL server error" });
|
|
34
|
+
}
|
|
24
35
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"@nuxt/kit": "^4.2.2",
|
|
43
43
|
"graphql": "^16.12.0",
|
|
44
44
|
"graphql-request": "^7.4.0",
|
|
45
|
-
"graphql-
|
|
45
|
+
"graphql-sse": "^2.6.0",
|
|
46
|
+
"graphql-yoga": "^5.18.0",
|
|
46
47
|
"jiti": "^2.6.1",
|
|
47
48
|
"ohash": "^2.0.11",
|
|
48
49
|
"tinyglobby": "^0.2.15"
|
|
@@ -59,8 +60,8 @@
|
|
|
59
60
|
"eslint": "^9.39.2",
|
|
60
61
|
"nuxt": "^4.2.2",
|
|
61
62
|
"typescript": "~5.9.3",
|
|
62
|
-
"vitest": "^4.0.
|
|
63
|
-
"vue-tsc": "^3.1
|
|
63
|
+
"vitest": "^4.0.16",
|
|
64
|
+
"vue-tsc": "^3.2.1"
|
|
64
65
|
},
|
|
65
66
|
"publishConfig": {
|
|
66
67
|
"access": "public"
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
|
|
2
|
-
export declare function useGraphQL(): {
|
|
3
|
-
request: <TResult, TVariables extends Record<string, unknown> | undefined>(document: TypedDocumentNode<TResult, TVariables>, variables?: TVariables) => Promise<TResult>;
|
|
4
|
-
};
|
|
File without changes
|
|
File without changes
|