nitro-graphql 1.1.3 → 1.2.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.
package/README.md CHANGED
@@ -31,8 +31,22 @@
31
31
  - 📦 **Optimized Bundling**: Smart chunking and dynamic imports for production
32
32
  - 🌐 **Nuxt Integration**: First-class Nuxt.js support with dedicated module
33
33
  - 🎭 **Custom Directives**: Create reusable GraphQL directives with automatic schema generation
34
+ - 🔗 **Multi-Service Support**: Connect to multiple external GraphQL APIs alongside your main server
34
35
 
35
- ## 🚀 Quick Start
36
+ ## 🎯 Used Projects
37
+
38
+ Projects using Nitro GraphQL in production:
39
+
40
+ - [**Nitroping**](https://github.com/productdevbook/nitroping) - Open-source, self-hosted push notification service
41
+
42
+ ## 🎥 Video Tutorials
43
+
44
+ Learn how to use Nitro GraphQL with these video tutorials:
45
+
46
+ - [**Nuxt 4 Usage**](https://x.com/productdevbook/status/1947314569531076633) - How to integrate Nitro GraphQL with Nuxt 4
47
+ - [**Nitro Usage**](https://x.com/productdevbook/status/1945759751393976348) - How to use Nitro GraphQL with standalone Nitro
48
+
49
+ ## 🎯 Quick Start
36
50
 
37
51
  ### Step 1: Installation
38
52
 
@@ -1260,6 +1274,249 @@ Help us improve nitro-graphql! Pick any item and contribute:
1260
1274
  > [!NOTE]
1261
1275
  > Have other ideas? Open an issue to discuss!
1262
1276
 
1277
+ ## 🔗 Multi-Service GraphQL Support
1278
+
1279
+ Connect to multiple external GraphQL APIs alongside your main GraphQL server. Perfect for integrating with services like GitHub API, Shopify API, or any GraphQL endpoint.
1280
+
1281
+ ### Configuration
1282
+
1283
+ ```typescript
1284
+ // nuxt.config.ts (for Nuxt projects)
1285
+ export default defineNuxtConfig({
1286
+ nitro: {
1287
+ graphql: {
1288
+ framework: 'graphql-yoga',
1289
+ externalServices: [
1290
+ {
1291
+ name: 'countries',
1292
+ schema: 'https://countries.trevorblades.com',
1293
+ endpoint: 'https://countries.trevorblades.com',
1294
+ documents: ['app/graphql/external/countries/**/*.graphql'],
1295
+ headers: {
1296
+ // Optional: Add custom headers
1297
+ 'Authorization': 'Bearer your-token'
1298
+ }
1299
+ },
1300
+ {
1301
+ name: 'github',
1302
+ schema: 'https://api.github.com/graphql',
1303
+ endpoint: 'https://api.github.com/graphql',
1304
+ documents: ['app/graphql/external/github/**/*.graphql'],
1305
+ headers: () => ({
1306
+ // Dynamic headers with function
1307
+ 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
1308
+ })
1309
+ }
1310
+ ]
1311
+ }
1312
+ }
1313
+ })
1314
+ ```
1315
+
1316
+ ### Schema Download & Caching (Optional)
1317
+
1318
+ For better performance and offline development, you can download and cache external schemas locally:
1319
+
1320
+ ```typescript
1321
+ // nuxt.config.ts
1322
+ export default defineNuxtConfig({
1323
+ nitro: {
1324
+ graphql: {
1325
+ framework: 'graphql-yoga',
1326
+ externalServices: [
1327
+ {
1328
+ name: 'github',
1329
+ schema: 'https://docs.github.com/public/schema.docs.graphql',
1330
+ endpoint: 'https://api.github.com/graphql',
1331
+ downloadSchema: 'once', // Download mode (see options below)
1332
+ downloadPath: './schemas/github.graphql', // Optional: custom download path
1333
+ headers: () => ({
1334
+ 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
1335
+ })
1336
+ }
1337
+ ]
1338
+ }
1339
+ }
1340
+ })
1341
+ ```
1342
+
1343
+ **Download Modes:**
1344
+
1345
+ | Mode | Behavior | Use Case |
1346
+ |------|----------|----------|
1347
+ | `true` or `'once'` | Download only if file doesn't exist | **Offline-friendly development** |
1348
+ | `'always'` | Check for updates on every build | **Always stay up-to-date** |
1349
+ | `'manual'` | Never download automatically | **Full manual control** |
1350
+ | `false` | Disable schema downloading | **Always use remote** |
1351
+
1352
+ **Benefits:**
1353
+ - **Offline Development**: Work without internet connection after initial download
1354
+ - **Faster Builds**: No remote fetching on each build when using 'once' mode
1355
+ - **Version Control**: Commit downloaded schemas to track API changes
1356
+ - **Network Reliability**: Fallback to cached schema if remote is unavailable
1357
+
1358
+ **How it works:**
1359
+ - **'once' mode (recommended)**: Downloads schema only if file doesn't exist, then uses cached version
1360
+ - **'always' mode**: Checks for schema changes on every build using hash comparison
1361
+ - **'manual' mode**: User manages schema files manually, no automatic downloading
1362
+
1363
+ **File locations:**
1364
+ - Default: `.nitro/graphql/schemas/[serviceName].graphql`
1365
+ - Custom: Use `downloadPath` option to specify your preferred location
1366
+
1367
+ ### Usage
1368
+
1369
+ #### 1. Create External Service Queries
1370
+
1371
+ ```graphql
1372
+ <!-- app/graphql/external/countries/countries.graphql -->
1373
+ query GetCountries {
1374
+ countries {
1375
+ code
1376
+ name
1377
+ emoji
1378
+ continent {
1379
+ name
1380
+ }
1381
+ }
1382
+ }
1383
+
1384
+ query GetCountry($code: ID!) {
1385
+ country(code: $code) {
1386
+ code
1387
+ name
1388
+ capital
1389
+ currency
1390
+ }
1391
+ }
1392
+ ```
1393
+
1394
+ #### 2. Use Generated SDKs
1395
+
1396
+ ```typescript
1397
+ // Import from centralized index
1398
+ import { $sdk, $countriesSdk, $githubSdk } from '~/app/graphql'
1399
+
1400
+ // Or import directly from service folders
1401
+ import { $countriesSdk } from '~/app/graphql/countries/ofetch'
1402
+
1403
+ // Use in components
1404
+ const countries = await $countriesSdk.GetCountries()
1405
+ const country = await $countriesSdk.GetCountry({ code: 'US' })
1406
+
1407
+ // Your main service still works
1408
+ const users = await $sdk.GetUsers()
1409
+ ```
1410
+
1411
+ #### 3. Folder Structure
1412
+
1413
+ After configuration, your project structure becomes:
1414
+
1415
+ ```
1416
+ app/graphql/
1417
+ ├── index.ts # Centralized exports (auto-generated)
1418
+ ├── default/ # Your main GraphQL service
1419
+ │ ├── ofetch.ts # Main service client
1420
+ │ └── sdk.ts # Main service SDK
1421
+ ├── countries/ # External countries service
1422
+ │ ├── ofetch.ts # Countries service client
1423
+ │ └── sdk.ts # Countries service SDK
1424
+ ├── github/ # External GitHub service
1425
+ │ ├── ofetch.ts # GitHub service client
1426
+ │ └── sdk.ts # GitHub service SDK
1427
+ └── external/ # Your external service queries
1428
+ ├── countries/
1429
+ │ └── countries.graphql
1430
+ └── github/
1431
+ └── repositories.graphql
1432
+ ```
1433
+
1434
+ #### 4. TypeScript Support
1435
+
1436
+ Each service gets its own type definitions:
1437
+
1438
+ ```typescript
1439
+ // Types are automatically generated and available
1440
+ import type { GetCountriesQuery } from '#graphql/client/countries'
1441
+ import type { GetUsersQuery } from '#graphql/client'
1442
+
1443
+ const handleCountries = (countries: GetCountriesQuery) => {
1444
+ // Fully typed countries data
1445
+ }
1446
+ ```
1447
+
1448
+ ### Service Configuration Options
1449
+
1450
+ | Option | Type | Required | Description |
1451
+ |--------|------|----------|-------------|
1452
+ | `name` | `string` | ✅ | Unique service name (used for folder/file names) |
1453
+ | `schema` | `string` \| `string[]` | ✅ | GraphQL schema URL or file path |
1454
+ | `endpoint` | `string` | ✅ | GraphQL endpoint URL for queries |
1455
+ | `documents` | `string[]` | ❌ | Glob patterns for GraphQL query files |
1456
+ | `headers` | `Record<string, string>` \| `() => Record<string, string>` | ❌ | Custom headers for schema introspection and queries |
1457
+ | `codegen.client` | `CodegenClientConfig` | ❌ | Custom codegen configuration for client types |
1458
+ | `codegen.clientSDK` | `GenericSdkConfig` | ❌ | Custom codegen configuration for SDK generation |
1459
+
1460
+ ## 🛠️ GraphQL Config (Optional but Recommended)
1461
+
1462
+ To enable GraphQL language features in your IDE (autocompletion, validation, go-to definition), create a `graphql.config.ts` file in your project root:
1463
+
1464
+ ### For Single Service (Main GraphQL Server Only)
1465
+
1466
+ ```typescript
1467
+ // graphql.config.ts
1468
+ import type { IGraphQLConfig } from 'graphql-config'
1469
+
1470
+ export default <IGraphQLConfig> {
1471
+ schema: ['./.nuxt/graphql/schema.graphql'],
1472
+ documents: ['./app/graphql/**/*.{graphql,js,ts,jsx,tsx}'],
1473
+ exclude: ['./app/graphql/external/**/*'] // Exclude external service documents
1474
+ }
1475
+ ```
1476
+
1477
+ ### For Multi-Service Setup
1478
+
1479
+ ```typescript
1480
+ // graphql.config.ts
1481
+ import type { IGraphQLConfig } from 'graphql-config'
1482
+
1483
+ export default <IGraphQLConfig> {
1484
+ projects: {
1485
+ // Main GraphQL server
1486
+ default: {
1487
+ schema: ['./.nuxt/graphql/schema.graphql'],
1488
+ documents: ['./app/graphql/default/**/*.{graphql,js,ts,jsx,tsx}']
1489
+ },
1490
+ // External services
1491
+ github: {
1492
+ schema: [
1493
+ // Use downloaded schema if available, otherwise use remote
1494
+ './.nuxt/graphql/schemas/github.graphql',
1495
+ // Fallback to remote if local doesn't exist
1496
+ 'https://docs.github.com/public/schema.docs.graphql'
1497
+ ],
1498
+ documents: ['./app/graphql/external/github/**/*.graphql']
1499
+ },
1500
+ countries: {
1501
+ schema: ['./.nuxt/graphql/schemas/countries.graphql'],
1502
+ documents: ['./app/graphql/external/countries/**/*.graphql']
1503
+ }
1504
+ }
1505
+ }
1506
+ ```
1507
+
1508
+ ### Schema Paths for Different Download Modes
1509
+
1510
+ - **Downloaded schemas**: `./.nuxt/graphql/schemas/[serviceName].graphql`
1511
+ - **Custom download path**: Use your `downloadPath` configuration
1512
+ - **Remote fallback**: Include remote URL as second option
1513
+
1514
+ This configuration enables:
1515
+ - 🎯 **Service-specific validation**: Each GraphQL service gets its own validation rules
1516
+ - 🚀 **IDE autocompletion**: Full IntelliSense for queries and mutations
1517
+ - ✅ **Real-time validation**: Catch GraphQL errors while typing
1518
+ - 🔍 **Go-to definition**: Navigate to type definitions across services
1519
+
1263
1520
  ## 🛠️ VS Code Extensions
1264
1521
 
1265
1522
  For the best development experience with GraphQL, install these recommended VS Code extensions:
@@ -1,7 +1,7 @@
1
- import * as _nuxt_schema0 from "@nuxt/schema";
1
+ import * as _nuxt_schema3 from "@nuxt/schema";
2
2
 
3
3
  //#region src/ecosystem/nuxt.d.ts
4
4
  interface ModuleOptions {}
5
- declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, ModuleOptions, false>;
5
+ declare const _default: _nuxt_schema3.NuxtModule<ModuleOptions, ModuleOptions, false>;
6
6
  //#endregion
7
7
  export { ModuleOptions, _default as default };
@@ -15,11 +15,19 @@ var nuxt_default = defineNuxtModule({
15
15
  options.tsConfig.compilerOptions ??= {};
16
16
  options.tsConfig.compilerOptions.paths ??= {};
17
17
  options.tsConfig.compilerOptions.paths["#graphql/client"] = ["./types/nitro-graphql-client.d.ts"];
18
+ const externalServices$1 = nuxt.options.nitro?.graphql?.externalServices || [];
19
+ for (const service of externalServices$1) {
20
+ options.references.push({ path: `types/nitro-graphql-client-${service.name}.d.ts` });
21
+ options.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [`./types/nitro-graphql-client-${service.name}.d.ts`];
22
+ }
18
23
  options.tsConfig.include = options.tsConfig.include || [];
19
24
  options.tsConfig.include.push("./types/nitro-graphql-client.d.ts");
25
+ for (const service of externalServices$1) options.tsConfig.include.push(`./types/nitro-graphql-client-${service.name}.d.ts`);
20
26
  });
21
27
  nuxt.options.alias = nuxt.options.alias || {};
22
28
  nuxt.options.alias["#graphql/client"] = join(nuxt.options.buildDir, "types/nitro-graphql-client.d.ts");
29
+ const externalServices = nuxt.options.nitro?.graphql?.externalServices || [];
30
+ for (const service of externalServices) nuxt.options.alias[`#graphql/client/${service.name}`] = join(nuxt.options.buildDir, `types/nitro-graphql-client-${service.name}.d.ts`);
23
31
  nuxt.hook("imports:dirs", (dirs) => {
24
32
  dirs.push(resolve(nuxt.options.srcDir, "graphql"));
25
33
  });
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { StandardSchemaV1 } from "./types/standard-schema.js";
2
- import { CodegenClientConfig, CodegenServerConfig, GenImport, GenericSdkConfig, NitroGraphQLOptions } from "./types/index.js";
3
- import * as nitropack1 from "nitropack";
2
+ import { CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, GenImport, GenericSdkConfig, NitroGraphQLOptions } from "./types/index.js";
3
+ import * as nitropack2 from "nitropack";
4
4
 
5
5
  //#region src/index.d.ts
6
- declare const _default: nitropack1.NitroModule;
6
+ declare const _default: nitropack2.NitroModule;
7
7
  //#endregion
8
- export { CodegenClientConfig, CodegenServerConfig, GenImport, GenericSdkConfig, NitroGraphQLOptions, StandardSchemaV1, _default as default };
8
+ export { CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, GenImport, GenericSdkConfig, NitroGraphQLOptions, StandardSchemaV1, _default as default };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { generateDirectiveSchemas } from "./utils/directive-parser.js";
2
- import { relativeWithDot, scanDirectives, scanDocs, scanResolvers, scanSchemas } from "./utils/index.js";
2
+ import { relativeWithDot, scanDirectives, scanDocs, scanResolvers, scanSchemas, validateExternalServices } from "./utils/index.js";
3
3
  import { clientTypeGeneration, serverTypeGeneration } from "./utils/type-generation.js";
4
4
  import { rollupConfig } from "./rollup.js";
5
5
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
@@ -15,6 +15,15 @@ var src_default = defineNitroModule({
15
15
  name: "nitro-graphql",
16
16
  async setup(nitro) {
17
17
  if (!nitro.options.graphql?.framework) consola.warn("No GraphQL framework specified. Please set graphql.framework to \"graphql-yoga\" or \"apollo-server\".");
18
+ if (nitro.options.graphql?.externalServices?.length) {
19
+ const validationErrors = validateExternalServices(nitro.options.graphql.externalServices);
20
+ if (validationErrors.length > 0) {
21
+ consola.error("External services configuration errors:");
22
+ for (const error of validationErrors) consola.error(` - ${error}`);
23
+ throw new Error("Invalid external services configuration");
24
+ }
25
+ consola.info(`Configured ${nitro.options.graphql.externalServices.length} external GraphQL services`);
26
+ }
18
27
  nitro.graphql ||= {
19
28
  buildDir: "",
20
29
  watchDirs: [],
@@ -60,6 +69,13 @@ var src_default = defineNitroModule({
60
69
  break;
61
70
  default:
62
71
  }
72
+ if (nitro.options.graphql?.externalServices?.length) {
73
+ for (const service of nitro.options.graphql.externalServices) if (service.documents?.length) for (const pattern of service.documents) {
74
+ const baseDir = pattern.split("**")[0].replace(/\/$/, "") || ".";
75
+ const resolvedDir = resolve(nitro.options.rootDir, baseDir);
76
+ if (!watchDirs.includes(resolvedDir)) watchDirs.push(resolvedDir);
77
+ }
78
+ }
63
79
  const watcher = watch(watchDirs, {
64
80
  persistent: true,
65
81
  ignoreInitial: true,
@@ -160,8 +176,14 @@ var src_default = defineNitroModule({
160
176
  types.tsConfig.compilerOptions.paths["#graphql/server"] = [relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-server.d.ts"))];
161
177
  types.tsConfig.compilerOptions.paths["#graphql/client"] = [relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-client.d.ts"))];
162
178
  types.tsConfig.compilerOptions.paths["#graphql/schema"] = [relativeWithDot(tsconfigDir, join(nitro.graphql.serverDir, "schema.ts"))];
179
+ if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) types.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [relativeWithDot(tsconfigDir, join(typesDir, `nitro-graphql-client-${service.name}.d.ts`))];
163
180
  types.tsConfig.include = types.tsConfig.include || [];
164
181
  types.tsConfig.include.push(relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-server.d.ts")), relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-client.d.ts")), relativeWithDot(tsconfigDir, join(typesDir, "graphql.d.ts")));
182
+ if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) types.tsConfig.include.push(relativeWithDot(tsconfigDir, join(typesDir, `nitro-graphql-client-${service.name}.d.ts`)));
183
+ });
184
+ if (nitro.options.framework?.name === "nuxt" && nitro.options.graphql?.externalServices?.length) nitro.hooks.hook("build:before", () => {
185
+ const nuxtOptions = nitro._nuxt?.options;
186
+ if (nuxtOptions) nuxtOptions.nitroGraphqlExternalServices = nitro.options.graphql?.externalServices || [];
165
187
  });
166
188
  if (!existsSync(join(nitro.options.rootDir, "graphql.config.ts"))) {
167
189
  const schemaPath = relativeWithDot(nitro.options.rootDir, resolve(nitro.graphql.buildDir, "schema.graphql"));
@@ -1,6 +1,6 @@
1
- import * as h34 from "h3";
1
+ import * as h36 from "h3";
2
2
 
3
3
  //#region src/routes/apollo-server.d.ts
4
- declare const _default: h34.EventHandler<h34.EventHandlerRequest, any>;
4
+ declare const _default: h36.EventHandler<h36.EventHandlerRequest, any>;
5
5
  //#endregion
6
6
  export { _default as default };
@@ -1,6 +1,6 @@
1
- import * as h36 from "h3";
1
+ import * as h30 from "h3";
2
2
 
3
3
  //#region src/routes/graphql-yoga.d.ts
4
- declare const _default: h36.EventHandler<h36.EventHandlerRequest, Promise<Response>>;
4
+ declare const _default: h30.EventHandler<h30.EventHandlerRequest, Promise<Response>>;
5
5
  //#endregion
6
6
  export { _default as default };
@@ -1,7 +1,7 @@
1
- import * as h32 from "h3";
1
+ import * as h34 from "h3";
2
2
 
3
3
  //#region src/routes/health.d.ts
4
- declare const _default: h32.EventHandler<h32.EventHandlerRequest, Promise<{
4
+ declare const _default: h34.EventHandler<h34.EventHandlerRequest, Promise<{
5
5
  status: string;
6
6
  message: string;
7
7
  timestamp: string;
@@ -57,6 +57,33 @@ declare module 'nitropack' {
57
57
  graphql?: NitroGraphQLOptions;
58
58
  }
59
59
  }
60
+ interface ExternalGraphQLService {
61
+ /** Unique name for this service (used for file naming and type generation) */
62
+ name: string;
63
+ /** Schema source - can be URL(s) for remote schemas or file path(s) for local schemas */
64
+ schema: string | string[];
65
+ /** GraphQL endpoint for this service */
66
+ endpoint: string;
67
+ /** Optional headers for schema introspection and client requests */
68
+ headers?: Record<string, string> | (() => Record<string, string>);
69
+ /** Optional: specific document patterns for this service */
70
+ documents?: string[];
71
+ /**
72
+ * Optional: Download and cache schema locally for offline usage
73
+ * - true or 'once': Download if file doesn't exist, then use cached version (offline-friendly)
74
+ * - 'always': Check for updates on every build (current behavior)
75
+ * - 'manual': Never download automatically, user manages schema files manually
76
+ * - false: Disable schema downloading
77
+ */
78
+ downloadSchema?: boolean | 'once' | 'always' | 'manual';
79
+ /** Optional: Custom path to save downloaded schema (default: .nitro/graphql/schemas/[serviceName].graphql) */
80
+ downloadPath?: string;
81
+ /** Optional: service-specific codegen configuration */
82
+ codegen?: {
83
+ client?: CodegenClientConfig;
84
+ clientSDK?: GenericSdkConfig;
85
+ };
86
+ }
60
87
  interface NitroGraphQLOptions {
61
88
  framework: 'graphql-yoga' | 'apollo-server';
62
89
  endpoint?: {
@@ -76,6 +103,8 @@ interface NitroGraphQLOptions {
76
103
  client?: CodegenClientConfig;
77
104
  clientSDK?: GenericSdkConfig;
78
105
  };
106
+ /** External GraphQL services to generate types and SDKs for */
107
+ externalServices?: ExternalGraphQLService[];
79
108
  }
80
109
  //#endregion
81
- export { CodegenClientConfig, CodegenServerConfig, GenImport, GenericSdkConfig, NitroGraphQLOptions };
110
+ export { CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, GenImport, GenericSdkConfig, NitroGraphQLOptions };
@@ -1,4 +1,4 @@
1
- import { CodegenClientConfig, GenericSdkConfig } from "../types/index.js";
1
+ import { CodegenClientConfig, ExternalGraphQLService, GenericSdkConfig } from "../types/index.js";
2
2
  import { GraphQLSchema } from "graphql";
3
3
  import { Source } from "@graphql-tools/utils";
4
4
  import { LoadSchemaOptions, UnnormalizedTypeDefPointer } from "@graphql-tools/load";
@@ -14,10 +14,25 @@ type GraphQLTypeDefPointer = UnnormalizedTypeDefPointer | UnnormalizedTypeDefPoi
14
14
  */
15
15
  type GraphQLLoadSchemaOptions = Partial<LoadSchemaOptions>;
16
16
  declare function graphQLLoadSchemaSync(schemaPointers: GraphQLTypeDefPointer, data?: GraphQLLoadSchemaOptions): Promise<GraphQLSchema | undefined>;
17
+ /**
18
+ * Load schema from external GraphQL service
19
+ */
20
+ declare function loadExternalSchema(service: ExternalGraphQLService, buildDir?: string): Promise<GraphQLSchema | undefined>;
21
+ /**
22
+ * Download and save schema from external service
23
+ */
24
+ declare function downloadAndSaveSchema(service: ExternalGraphQLService, buildDir: string): Promise<string | undefined>;
17
25
  declare function loadGraphQLDocuments(patterns: string | string[]): Promise<Source[]>;
18
- declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string): Promise<false | {
26
+ declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string, serviceName?: string): Promise<false | {
19
27
  types: string;
20
28
  sdk: string;
21
29
  }>;
30
+ /**
31
+ * Generate client types for external GraphQL service
32
+ */
33
+ declare function generateExternalClientTypes(service: ExternalGraphQLService, schema: GraphQLSchema, docs: Source[]): Promise<{
34
+ types: string;
35
+ sdk: string;
36
+ } | false>;
22
37
  //#endregion
23
- export { GraphQLLoadSchemaOptions, GraphQLTypeDefPointer, generateClientTypes, graphQLLoadSchemaSync, loadGraphQLDocuments };
38
+ export { GraphQLLoadSchemaOptions, GraphQLTypeDefPointer, downloadAndSaveSchema, generateClientTypes, generateExternalClientTypes, graphQLLoadSchemaSync, loadExternalSchema, loadGraphQLDocuments };
@@ -1,14 +1,18 @@
1
1
  import { preset } from "../node_modules/.pnpm/@graphql-codegen_import-types-preset@3.0.1_graphql@16.11.0/node_modules/@graphql-codegen/import-types-preset/esm/index.js";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
3
  import { consola as consola$1 } from "consola";
3
4
  import { defu as defu$1 } from "defu";
5
+ import { dirname, resolve } from "pathe";
4
6
  import { parse } from "graphql";
5
7
  import { printSchemaWithDirectives } from "@graphql-tools/utils";
8
+ import { createHash } from "node:crypto";
6
9
  import { codegen } from "@graphql-codegen/core";
7
10
  import { plugin } from "@graphql-codegen/typescript";
8
11
  import { plugin as plugin$1 } from "@graphql-codegen/typescript-generic-sdk";
9
12
  import { plugin as plugin$2 } from "@graphql-codegen/typescript-operations";
10
13
  import { GraphQLFileLoader } from "@graphql-tools/graphql-file-loader";
11
14
  import { loadDocuments, loadSchemaSync } from "@graphql-tools/load";
15
+ import { UrlLoader } from "@graphql-tools/url-loader";
12
16
  import { CurrencyResolver, DateTimeISOResolver, DateTimeResolver, JSONObjectResolver, JSONResolver, NonEmptyStringResolver, UUIDResolver } from "graphql-scalars";
13
17
 
14
18
  //#region src/utils/client-codegen.ts
@@ -31,7 +35,11 @@ async function graphQLLoadSchemaSync(schemaPointers, data = {}) {
31
35
  try {
32
36
  result = loadSchemaSync(filteredPointers, {
33
37
  ...data,
34
- loaders: [new GraphQLFileLoader(), ...data.loaders || []]
38
+ loaders: [
39
+ new GraphQLFileLoader(),
40
+ new UrlLoader(),
41
+ ...data.loaders || []
42
+ ]
35
43
  });
36
44
  } catch (e) {
37
45
  if ((e.message || "").includes("Unable to find any GraphQL type definitions for the following pointers:")) consola$1.info("No server GraphQL files found. If you need server-side GraphQL, add .graphql files to your server directory.");
@@ -39,6 +47,92 @@ async function graphQLLoadSchemaSync(schemaPointers, data = {}) {
39
47
  }
40
48
  return result;
41
49
  }
50
+ /**
51
+ * Load schema from external GraphQL service
52
+ */
53
+ async function loadExternalSchema(service, buildDir) {
54
+ try {
55
+ const headers = typeof service.headers === "function" ? service.headers() : service.headers || {};
56
+ const schemas = Array.isArray(service.schema) ? service.schema : [service.schema];
57
+ if (service.downloadSchema && buildDir) {
58
+ const defaultPath = resolve(buildDir, "graphql", "schemas", `${service.name}.graphql`);
59
+ const schemaFilePath = service.downloadPath ? resolve(service.downloadPath) : defaultPath;
60
+ if (existsSync(schemaFilePath)) {
61
+ consola$1.info(`[graphql:${service.name}] Loading schema from local file: ${schemaFilePath}`);
62
+ try {
63
+ const result$1 = loadSchemaSync([schemaFilePath], { loaders: [new GraphQLFileLoader()] });
64
+ consola$1.info(`[graphql:${service.name}] External schema loaded successfully from local file`);
65
+ return result$1;
66
+ } catch (localError) {
67
+ consola$1.warn(`[graphql:${service.name}] Failed to load local schema, falling back to remote:`, localError);
68
+ }
69
+ }
70
+ }
71
+ consola$1.info(`[graphql:${service.name}] Loading external schema from: ${schemas.join(", ")}`);
72
+ const result = loadSchemaSync(schemas, {
73
+ loaders: [new GraphQLFileLoader(), new UrlLoader()],
74
+ ...Object.keys(headers).length > 0 && { headers }
75
+ });
76
+ consola$1.info(`[graphql:${service.name}] External schema loaded successfully`);
77
+ return result;
78
+ } catch (error) {
79
+ consola$1.error(`[graphql:${service.name}] Failed to load external schema:`, error);
80
+ return void 0;
81
+ }
82
+ }
83
+ /**
84
+ * Download and save schema from external service
85
+ */
86
+ async function downloadAndSaveSchema(service, buildDir) {
87
+ const downloadMode = service.downloadSchema;
88
+ if (!downloadMode || downloadMode === "manual") return void 0;
89
+ const defaultPath = resolve(buildDir, "graphql", "schemas", `${service.name}.graphql`);
90
+ const schemaFilePath = service.downloadPath ? resolve(service.downloadPath) : defaultPath;
91
+ try {
92
+ const headers = typeof service.headers === "function" ? service.headers() : service.headers || {};
93
+ const schemas = Array.isArray(service.schema) ? service.schema : [service.schema];
94
+ let shouldDownload = false;
95
+ const fileExists = existsSync(schemaFilePath);
96
+ if (downloadMode === "always") {
97
+ shouldDownload = true;
98
+ if (fileExists) try {
99
+ const remoteSchema = loadSchemaSync(schemas, {
100
+ loaders: [new UrlLoader()],
101
+ ...Object.keys(headers).length > 0 && { headers }
102
+ });
103
+ const remoteSchemaString = printSchemaWithDirectives(remoteSchema);
104
+ const remoteHash = createHash("md5").update(remoteSchemaString).digest("hex");
105
+ const localSchemaString = readFileSync(schemaFilePath, "utf-8");
106
+ const localHash = createHash("md5").update(localSchemaString).digest("hex");
107
+ if (remoteHash === localHash) {
108
+ shouldDownload = false;
109
+ consola$1.info(`[graphql:${service.name}] Schema is up-to-date, using cached version`);
110
+ }
111
+ } catch {
112
+ consola$1.warn(`[graphql:${service.name}] Unable to compare with remote schema, updating local cache`);
113
+ shouldDownload = true;
114
+ }
115
+ } else if (downloadMode === true || downloadMode === "once") {
116
+ shouldDownload = !fileExists;
117
+ if (fileExists) consola$1.info(`[graphql:${service.name}] Using cached schema from: ${schemaFilePath}`);
118
+ }
119
+ if (shouldDownload) {
120
+ consola$1.info(`[graphql:${service.name}] Downloading schema to: ${schemaFilePath}`);
121
+ const schema = loadSchemaSync(schemas, {
122
+ loaders: [new UrlLoader()],
123
+ ...Object.keys(headers).length > 0 && { headers }
124
+ });
125
+ const schemaString = printSchemaWithDirectives(schema);
126
+ mkdirSync(dirname(schemaFilePath), { recursive: true });
127
+ writeFileSync(schemaFilePath, schemaString, "utf-8");
128
+ consola$1.success(`[graphql:${service.name}] Schema downloaded and saved successfully`);
129
+ }
130
+ return schemaFilePath;
131
+ } catch (error) {
132
+ consola$1.error(`[graphql:${service.name}] Failed to download schema:`, error);
133
+ return void 0;
134
+ }
135
+ }
42
136
  async function loadGraphQLDocuments(patterns) {
43
137
  try {
44
138
  const result = await loadDocuments(patterns, { loaders: [new GraphQLFileLoader()] });
@@ -48,12 +142,14 @@ async function loadGraphQLDocuments(patterns) {
48
142
  else throw e;
49
143
  }
50
144
  }
51
- async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath) {
145
+ async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName) {
52
146
  if (docs.length === 0) {
53
- consola$1.info("[graphql] No client GraphQL files found. Skipping client type generation.");
147
+ const serviceLabel$1 = serviceName ? `:${serviceName}` : "";
148
+ consola$1.info(`[graphql${serviceLabel$1}] No client GraphQL files found. Skipping client type generation.`);
54
149
  return false;
55
150
  }
56
- consola$1.info(`[graphql] Found ${docs.length} client GraphQL documents`);
151
+ const serviceLabel = serviceName ? `:${serviceName}` : "";
152
+ consola$1.info(`[graphql${serviceLabel}] Found ${docs.length} client GraphQL documents`);
57
153
  const defaultConfig = {
58
154
  emitLegacyCommonJSImports: false,
59
155
  useTypeImports: true,
@@ -98,12 +194,13 @@ async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, ou
98
194
  typescriptOperations: { plugin: plugin$2 }
99
195
  }
100
196
  });
197
+ const typesPath = serviceName ? `#graphql/client/${serviceName}` : "#graphql/client";
101
198
  const sdkOutput = await preset.buildGeneratesSection({
102
199
  baseOutputDir: outputPath || "client-types.generated.ts",
103
200
  schema: parse(printSchemaWithDirectives(schema)),
104
201
  documents: [...docs],
105
202
  config: mergedSdkConfig,
106
- presetConfig: { typesPath: "#graphql/client" },
203
+ presetConfig: { typesPath },
107
204
  plugins: [{ pluginContent: {} }, { typescriptGenericSdk: {} }],
108
205
  pluginMap: {
109
206
  pluginContent: { plugin: pluginContent },
@@ -116,15 +213,24 @@ async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, ou
116
213
  content: await codegen(config$1)
117
214
  };
118
215
  }));
216
+ const sdkContent = results[0]?.content || "";
119
217
  return {
120
218
  types: output,
121
- sdk: results[0]?.content || ""
219
+ sdk: sdkContent
122
220
  };
123
221
  } catch (error) {
124
- consola$1.warn("[graphql] Client type generation failed:", error);
222
+ consola$1.warn(`[graphql${serviceLabel}] Client type generation failed:`, error);
125
223
  return false;
126
224
  }
127
225
  }
226
+ /**
227
+ * Generate client types for external GraphQL service
228
+ */
229
+ async function generateExternalClientTypes(service, schema, docs) {
230
+ const config = service.codegen?.client || {};
231
+ const sdkConfig = service.codegen?.clientSDK || {};
232
+ return generateClientTypes(schema, docs, config, sdkConfig, void 0, service.name);
233
+ }
128
234
 
129
235
  //#endregion
130
- export { generateClientTypes, graphQLLoadSchemaSync, loadGraphQLDocuments };
236
+ export { downloadAndSaveSchema, generateClientTypes, generateExternalClientTypes, graphQLLoadSchemaSync, loadExternalSchema, loadGraphQLDocuments };
@@ -149,10 +149,11 @@ var DirectiveParser = class {
149
149
  for (const prop of node.properties || []) {
150
150
  if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
151
151
  switch (prop.key.name) {
152
- case "type":
152
+ case "type": {
153
153
  const typeValue = this.extractStringLiteral(prop.value);
154
154
  if (typeValue) type = typeValue;
155
155
  break;
156
+ }
156
157
  case "defaultValue":
157
158
  defaultValue = this.extractLiteralValue(prop.value);
158
159
  break;
@@ -198,9 +199,10 @@ async function generateDirectiveSchemas(nitro, directives) {
198
199
  const { resolve } = await import("pathe");
199
200
  const directiveSchemas = [];
200
201
  const seenDirectives = /* @__PURE__ */ new Set();
202
+ const parser = new DirectiveParser();
201
203
  for (const dir of directives) for (const _imp of dir.imports) {
202
204
  const fileContent = await readFile(dir.specifier, "utf-8");
203
- const directiveDefs = await directiveParser.parseDirectives(fileContent, dir.specifier);
205
+ const directiveDefs = await parser.parseDirectives(fileContent, dir.specifier);
204
206
  for (const def of directiveDefs) {
205
207
  if (seenDirectives.has(def.name)) continue;
206
208
  seenDirectives.add(def.name);
@@ -12,5 +12,13 @@ declare function scanDirectives(nitro: Nitro): Promise<GenImport[]>;
12
12
  declare function scanTypeDefs(nitro: Nitro): Promise<string[]>;
13
13
  declare function scanSchemas(nitro: Nitro): Promise<string[]>;
14
14
  declare function scanDocs(nitro: Nitro): Promise<string[]>;
15
+ /**
16
+ * Scan documents for a specific external service
17
+ */
18
+ declare function scanExternalServiceDocs(nitro: Nitro, serviceName: string, patterns: string[]): Promise<string[]>;
19
+ /**
20
+ * Validate external GraphQL service configuration
21
+ */
22
+ declare function validateExternalServices(services: any[]): string[];
15
23
  //#endregion
16
- export { GLOB_SCAN_PATTERN, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, getImportId, relativeWithDot, scanDirectives, scanDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs };
24
+ export { GLOB_SCAN_PATTERN, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, getImportId, relativeWithDot, scanDirectives, scanDocs, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs, validateExternalServices };
@@ -104,7 +104,48 @@ async function scanSchemas(nitro) {
104
104
  }
105
105
  async function scanDocs(nitro) {
106
106
  const files = await scanDir(nitro, nitro.options.rootDir, nitro.graphql.dir.client, "**/*.graphql");
107
- return files.map((f) => f.fullPath);
107
+ return files.filter((f) => !f.path.startsWith("external/")).map((f) => f.fullPath);
108
+ }
109
+ /**
110
+ * Scan documents for a specific external service
111
+ */
112
+ async function scanExternalServiceDocs(nitro, serviceName, patterns) {
113
+ if (!patterns.length) return [];
114
+ const files = [];
115
+ for (const pattern of patterns) try {
116
+ const serviceFiles = await glob(pattern, {
117
+ cwd: nitro.options.rootDir,
118
+ dot: true,
119
+ ignore: nitro.options.ignore,
120
+ absolute: true
121
+ });
122
+ files.push(...serviceFiles);
123
+ } catch (error) {
124
+ nitro.logger.warn(`[graphql:${serviceName}] Error scanning documents with pattern "${pattern}":`, error);
125
+ }
126
+ return files.filter((file, index, self) => self.indexOf(file) === index);
127
+ }
128
+ /**
129
+ * Validate external GraphQL service configuration
130
+ */
131
+ function validateExternalServices(services) {
132
+ const errors = [];
133
+ const serviceNames = /* @__PURE__ */ new Set();
134
+ for (const [index, service] of services.entries()) {
135
+ const prefix = `externalServices[${index}]`;
136
+ if (!service.name || typeof service.name !== "string") errors.push(`${prefix}.name is required and must be a string`);
137
+ else if (serviceNames.has(service.name)) errors.push(`${prefix}.name "${service.name}" must be unique`);
138
+ else serviceNames.add(service.name);
139
+ if (!service.schema) errors.push(`${prefix}.schema is required`);
140
+ if (!service.endpoint || typeof service.endpoint !== "string") errors.push(`${prefix}.endpoint is required and must be a string`);
141
+ else try {
142
+ const url = new URL(service.endpoint);
143
+ } catch {
144
+ errors.push(`${prefix}.endpoint "${service.endpoint}" must be a valid URL`);
145
+ }
146
+ if (service.name && !/^[a-z]\w*$/i.test(service.name)) errors.push(`${prefix}.name "${service.name}" must be a valid identifier (letters, numbers, underscore, starting with letter)`);
147
+ }
148
+ return errors;
108
149
  }
109
150
  async function scanFiles(nitro, name, globPattern = GLOB_SCAN_PATTERN) {
110
151
  const files = await Promise.all(nitro.options.scanDirs.map((dir) => scanDir(nitro, dir, name, globPattern))).then((r) => r.flat());
@@ -132,4 +173,4 @@ async function scanDir(nitro, dir, name, globPattern = GLOB_SCAN_PATTERN) {
132
173
  }
133
174
 
134
175
  //#endregion
135
- export { GLOB_SCAN_PATTERN, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, getImportId, relativeWithDot, scanDirectives, scanDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs };
176
+ export { GLOB_SCAN_PATTERN, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, getImportId, relativeWithDot, scanDirectives, scanDocs, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs, validateExternalServices };
@@ -1,4 +1,4 @@
1
- import { generateClientTypes, loadGraphQLDocuments } from "./client-codegen.js";
1
+ import { downloadAndSaveSchema, generateClientTypes, generateExternalClientTypes, loadExternalSchema, loadGraphQLDocuments } from "./client-codegen.js";
2
2
  import { generateTypes } from "./server-codegen.js";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import consola from "consola";
@@ -9,8 +9,27 @@ import { mergeTypeDefs } from "@graphql-tools/merge";
9
9
  import { printSchemaWithDirectives } from "@graphql-tools/utils";
10
10
 
11
11
  //#region src/utils/type-generation.ts
12
- function generateNuxtOfetchClient(clientDir) {
13
- const ofetchPath = resolve(clientDir, "ofetch.ts");
12
+ function generateGraphQLIndexFile(clientDir, externalServices = []) {
13
+ const indexPath = resolve(clientDir, "index.ts");
14
+ if (!existsSync(indexPath)) {
15
+ let indexContent = `// This file is auto-generated once by nitro-graphql for quick start
16
+ // You can modify this file according to your needs
17
+ //
18
+ // Export your main GraphQL service (auto-generated)
19
+ export * from './default/ofetch'
20
+
21
+ // Export external GraphQL services (auto-generated for existing services)
22
+ // When you add new external services, don't forget to add their exports here:
23
+ // export * from './yourServiceName/ofetch'
24
+ `;
25
+ for (const service of externalServices) indexContent += `export * from './${service.name}/ofetch'\n`;
26
+ writeFileSync(indexPath, indexContent, "utf-8");
27
+ }
28
+ }
29
+ function generateNuxtOfetchClient(clientDir, serviceName = "default") {
30
+ const serviceDir = resolve(clientDir, serviceName);
31
+ const ofetchPath = resolve(serviceDir, "ofetch.ts");
32
+ if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
14
33
  if (!existsSync(ofetchPath)) {
15
34
  const ofetchContent = `// This file is auto-generated once by nitro-graphql for quick start
16
35
  // You can modify this file according to your needs
@@ -38,6 +57,38 @@ export const $sdk = getSdk(createGraphQLClient('/api/graphql'))`;
38
57
  writeFileSync(ofetchPath, ofetchContent, "utf-8");
39
58
  }
40
59
  }
60
+ function generateExternalOfetchClient(clientDir, serviceName, endpoint) {
61
+ const serviceDir = resolve(clientDir, serviceName);
62
+ const ofetchPath = resolve(serviceDir, "ofetch.ts");
63
+ if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
64
+ if (!existsSync(ofetchPath)) {
65
+ const capitalizedServiceName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
66
+ const ofetchContent = `// This file is auto-generated once by nitro-graphql for quick start
67
+ // You can modify this file according to your needs
68
+ import type { Sdk, Requester } from './sdk'
69
+ import { getSdk } from './sdk'
70
+
71
+ export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
72
+ return async <R>(doc: string, vars?: any): Promise<R> => {
73
+ const headers = import.meta.server ? useRequestHeaders() : undefined
74
+
75
+ const result = await $fetch(endpoint, {
76
+ method: 'POST',
77
+ body: { query: doc, variables: vars },
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ ...headers,
81
+ },
82
+ })
83
+
84
+ return result as R
85
+ }
86
+ }
87
+
88
+ export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())`;
89
+ writeFileSync(ofetchPath, ofetchContent, "utf-8");
90
+ }
91
+ }
41
92
  async function serverTypeGeneration(app) {
42
93
  try {
43
94
  const schemas = app.scanSchemas || [];
@@ -65,28 +116,101 @@ async function serverTypeGeneration(app) {
65
116
  }
66
117
  async function clientTypeGeneration(nitro) {
67
118
  try {
68
- const docs = nitro.scanDocuments;
69
- const loadDocs = await loadGraphQLDocuments(docs);
70
- const schemaFilePath = join(nitro.graphql.buildDir, "schema.graphql");
71
- if (!existsSync(schemaFilePath)) {
72
- consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
73
- return;
74
- }
75
- const graphqlString = readFileSync(schemaFilePath, "utf-8");
76
- const schema = buildSchema(graphqlString);
77
- const types = await generateClientTypes(schema, loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {});
78
- if (types === false) return;
79
- const clientTypesPath = resolve(nitro.options.buildDir, "types", "nitro-graphql-client.d.ts");
80
- const sdkTypesPath = resolve(nitro.graphql.clientDir, "sdk.ts");
81
- mkdirSync(dirname(clientTypesPath), { recursive: true });
82
- writeFileSync(clientTypesPath, types.types, "utf-8");
83
- mkdirSync(dirname(sdkTypesPath), { recursive: true });
84
- writeFileSync(sdkTypesPath, types.sdk, "utf-8");
85
- if (nitro.options.framework?.name === "nuxt") generateNuxtOfetchClient(nitro.graphql.clientDir);
119
+ await generateMainClientTypes(nitro);
120
+ if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro);
86
121
  } catch (error) {
87
122
  consola.error("Client schema generation error:", error);
88
123
  }
89
124
  }
125
+ /**
126
+ * Check for old structure files and warn user about manual migration
127
+ */
128
+ function checkOldStructure(clientDir) {
129
+ const oldOfetchPath = resolve(clientDir, "ofetch.ts");
130
+ const oldSdkPath = resolve(clientDir, "sdk.ts");
131
+ if (existsSync(oldOfetchPath) || existsSync(oldSdkPath)) {
132
+ const foundFiles = [];
133
+ if (existsSync(oldOfetchPath)) foundFiles.push("app/graphql/ofetch.ts");
134
+ if (existsSync(oldSdkPath)) foundFiles.push("app/graphql/sdk.ts");
135
+ consola.error(`⚠️ OLD GRAPHQL STRUCTURE DETECTED!
136
+
137
+ 📁 Found old files in app/graphql/ directory that need to be moved:
138
+ • ${foundFiles.join("\n • ")}
139
+
140
+ 🔄 Please manually move these files to the new structure:
141
+ • app/graphql/ofetch.ts → app/graphql/default/ofetch.ts
142
+ • app/graphql/sdk.ts → app/graphql/default/sdk.ts
143
+
144
+ 📝 Also update your app/graphql/index.ts to include:
145
+ export * from './default/ofetch'
146
+
147
+ 💡 After moving, update your imports to use:
148
+ import { $sdk } from "#graphql/client"
149
+
150
+ 🚫 The old files will cause import conflicts until moved!`);
151
+ }
152
+ }
153
+ async function generateMainClientTypes(nitro) {
154
+ checkOldStructure(nitro.graphql.clientDir);
155
+ const docs = nitro.scanDocuments;
156
+ const loadDocs = await loadGraphQLDocuments(docs);
157
+ const schemaFilePath = join(nitro.graphql.buildDir, "schema.graphql");
158
+ if (!existsSync(schemaFilePath)) {
159
+ consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
160
+ return;
161
+ }
162
+ const graphqlString = readFileSync(schemaFilePath, "utf-8");
163
+ const schema = buildSchema(graphqlString);
164
+ const types = await generateClientTypes(schema, loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {});
165
+ if (types === false) return;
166
+ const clientTypesPath = resolve(nitro.options.buildDir, "types", "nitro-graphql-client.d.ts");
167
+ const defaultServiceDir = resolve(nitro.graphql.clientDir, "default");
168
+ const sdkTypesPath = resolve(defaultServiceDir, "sdk.ts");
169
+ mkdirSync(dirname(clientTypesPath), { recursive: true });
170
+ writeFileSync(clientTypesPath, types.types, "utf-8");
171
+ mkdirSync(defaultServiceDir, { recursive: true });
172
+ writeFileSync(sdkTypesPath, types.sdk, "utf-8");
173
+ if (nitro.options.framework?.name === "nuxt") {
174
+ generateNuxtOfetchClient(nitro.graphql.clientDir, "default");
175
+ const externalServices = nitro.options.graphql?.externalServices || [];
176
+ generateGraphQLIndexFile(nitro.graphql.clientDir, externalServices);
177
+ }
178
+ }
179
+ async function generateExternalServicesTypes(nitro) {
180
+ const externalServices = nitro.options.graphql?.externalServices || [];
181
+ for (const service of externalServices) try {
182
+ consola.info(`[graphql:${service.name}] Processing external service`);
183
+ await downloadAndSaveSchema(service, nitro.options.buildDir);
184
+ const schema = await loadExternalSchema(service, nitro.options.buildDir);
185
+ if (!schema) {
186
+ consola.warn(`[graphql:${service.name}] Failed to load schema, skipping`);
187
+ continue;
188
+ }
189
+ const documentPatterns = service.documents || [];
190
+ let loadDocs = [];
191
+ if (documentPatterns.length > 0) try {
192
+ loadDocs = await loadGraphQLDocuments(documentPatterns);
193
+ } catch (error) {
194
+ consola.warn(`[graphql:${service.name}] No documents found:`, error);
195
+ }
196
+ const types = await generateExternalClientTypes(service, schema, loadDocs);
197
+ if (types === false) {
198
+ consola.warn(`[graphql:${service.name}] Type generation failed`);
199
+ continue;
200
+ }
201
+ const serviceTypesPath = resolve(nitro.options.buildDir, "types", `nitro-graphql-client-${service.name}.d.ts`);
202
+ const serviceDir = resolve(nitro.graphql.clientDir, service.name);
203
+ const serviceSdkPath = resolve(serviceDir, "sdk.ts");
204
+ mkdirSync(dirname(serviceTypesPath), { recursive: true });
205
+ writeFileSync(serviceTypesPath, types.types, "utf-8");
206
+ mkdirSync(serviceDir, { recursive: true });
207
+ writeFileSync(serviceSdkPath, types.sdk, "utf-8");
208
+ if (nitro.options.framework?.name === "nuxt") generateExternalOfetchClient(nitro.graphql.clientDir, service.name, service.endpoint);
209
+ consola.success(`[graphql:${service.name}] External service types generated successfully`);
210
+ } catch (error) {
211
+ consola.error(`[graphql:${service.name}] External service generation failed:`, error);
212
+ }
213
+ }
90
214
 
91
215
  //#endregion
92
216
  export { clientTypeGeneration, serverTypeGeneration };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nitro-graphql",
3
3
  "type": "module",
4
- "version": "1.1.3",
4
+ "version": "1.2.0",
5
5
  "description": "GraphQL integration for Nitro",
6
6
  "license": "MIT",
7
7
  "sideEffects": false,
@@ -72,6 +72,7 @@
72
72
  "@graphql-tools/load-files": "^7.0.1",
73
73
  "@graphql-tools/merge": "^9.1.0",
74
74
  "@graphql-tools/schema": "^10.0.24",
75
+ "@graphql-tools/url-loader": "^8.0.33",
75
76
  "@graphql-tools/utils": "^10.9.0",
76
77
  "chokidar": "^4.0.3",
77
78
  "consola": "^3.4.2",
@@ -88,8 +89,8 @@
88
89
  "@antfu/eslint-config": "^4.17.0",
89
90
  "@apollo/server": "^5.0.0",
90
91
  "@graphql-codegen/import-types-preset": "^3.0.1",
91
- "@nuxt/kit": "4.0.0-rc.0",
92
- "@nuxt/schema": "4.0.0-rc.0",
92
+ "@nuxt/kit": "^4.0.3",
93
+ "@nuxt/schema": "^4.0.3",
93
94
  "@types/node": "^20.19.9",
94
95
  "bumpp": "^10.2.0",
95
96
  "changelogen": "^0.6.2",