@stonecrop/nuxt-grafserv 0.7.5 → 0.7.8

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,79 +31,169 @@ The module automatically registers these handlers:
31
31
 
32
32
  ## Quick Setup
33
33
 
34
- 1. Add `@stonecrop/nuxt-grafserv` dependency to your project:
34
+ ### PostGraphile Integration (Recommended)
35
+
36
+ For PostGraphile users, this is the recommended configuration approach:
37
+
38
+ 1. Add dependencies:
35
39
 
36
40
  ```bash
37
41
  # Using pnpm
38
- pnpm add @stonecrop/nuxt-grafserv
42
+ pnpm add @stonecrop/nuxt-grafserv postgraphile
39
43
 
40
44
  # Using yarn
41
- yarn add @stonecrop/nuxt-grafserv
45
+ yarn add @stonecrop/nuxt-grafserv postgraphile
42
46
 
43
47
  # Using npm
44
- npm install @stonecrop/nuxt-grafserv
48
+ npm install @stonecrop/nuxt-grafserv postgraphile
45
49
  ```
46
50
 
47
- 2. Add `@stonecrop/nuxt-grafserv` to the `modules` section of `nuxt.config.ts`:
51
+ 2. Configure in `nuxt.config.ts`:
48
52
 
49
53
  ```ts
54
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
55
+ import { makePgService } from 'postgraphile/adaptors/pg'
56
+
50
57
  export default defineNuxtConfig({
51
58
  modules: ['@stonecrop/nuxt-grafserv'],
52
59
  grafserv: {
60
+ type: 'postgraphile', // Required: specify configuration type
61
+ preset: {
62
+ extends: [PostGraphileAmberPreset],
63
+ pgServices: [
64
+ makePgService({
65
+ connectionString: process.env.DATABASE_URL || 'postgresql://localhost/mydb',
66
+ schemas: ['public'],
67
+ }),
68
+ ],
69
+ },
70
+ url: '/graphql',
71
+ graphiql: true,
72
+ }
73
+ })
74
+ ```
75
+
76
+ ### Custom Schema Configuration
77
+
78
+ For custom GraphQL schemas with your own resolvers:
79
+
80
+ 1. Add dependency:
81
+
82
+ ```bash
83
+ pnpm add @stonecrop/nuxt-grafserv
84
+ ```
85
+
86
+ 2. Configure in `nuxt.config.ts`:
87
+
88
+ ```ts
89
+ export default defineNuxtConfig({
90
+ modules: ['@stonecrop/nuxt-grafserv'],
91
+ grafserv: {
92
+ type: 'schema', // Required: specify configuration type
53
93
  schema: 'server/**/*.graphql',
54
94
  resolvers: 'server/resolvers.ts',
55
- url: '/graphql/', // Serves both GraphQL API and Ruru UI
95
+ url: '/graphql',
96
+ graphiql: true,
56
97
  }
57
98
  })
58
99
  ```
59
100
 
60
101
  ## Configuration
61
102
 
62
- ### All Available Options
103
+ The module supports two configuration types using a discriminated union pattern:
63
104
 
64
- | Option | Type | Default | Description |
65
- |--------|------|---------|-------------|
66
- | `schema` | `string \| string[] \| SchemaProvider` | `'server/**/*.graphql'` | Path(s) to GraphQL schema files or schema provider function |
67
- | `resolvers` | `string` | `'server/resolvers.ts'` | Path to resolvers file |
68
- | `url` | `string` | `'/graphql/'` | GraphQL endpoint URL (also serves Ruru UI) |
69
- | `graphiql` | `boolean` | `true` in dev, `false` in prod | Enable GraphiQL IDE |
70
- | `preset` | `GraphileConfig.Preset` | `undefined` | Custom Graphile preset for advanced configuration |
105
+ ### PostGraphile Configuration
71
106
 
72
- ### Full Configuration Example
107
+ Use `type: 'postgraphile'` for PostGraphile-based GraphQL APIs:
108
+
109
+ | Option | Type | Required | Description |
110
+ |--------|------|----------|-------------|
111
+ | `type` | `'postgraphile'` | ✅ | Configuration type discriminator |
112
+ | `preset` | `GraphileConfig.Preset` | ✅ | PostGraphile preset passed to makeSchema() |
113
+ | `url` | `string` | ❌ | GraphQL endpoint URL (default: '/graphql/') |
114
+ | `graphiql` | `boolean` | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |
115
+
116
+ **Example:**
73
117
 
74
118
  ```ts
119
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
120
+ import { makePgService } from 'postgraphile/adaptors/pg'
121
+
75
122
  export default defineNuxtConfig({
76
- modules: ['@stonecrop/nuxt-grafserv'],
77
123
  grafserv: {
78
- // Schema configuration
124
+ type: 'postgraphile',
125
+ preset: {
126
+ extends: [PostGraphileAmberPreset],
127
+ pgServices: [
128
+ makePgService({
129
+ connectionString: process.env.DATABASE_URL,
130
+ schemas: ['public'],
131
+ }),
132
+ ],
133
+ plugins: [MyCustomPlugin],
134
+ },
135
+ url: '/graphql',
136
+ graphiql: true,
137
+ }
138
+ })
139
+ ```
140
+
141
+ ### Schema Configuration
142
+
143
+ Use `type: 'schema'` for custom GraphQL schemas with Grafast resolvers:
144
+
145
+ | Option | Type | Required | Description |
146
+ |--------|------|----------|-------------|
147
+ | `type` | `'schema'` | ✅ | Configuration type discriminator |
148
+ | `schema` | `string \| string[] \| SchemaProvider` | ✅ | Path(s) to .graphql files or schema provider function |
149
+ | `resolvers` | `string` | ❌ | Path to resolvers file (required for .graphql files) |
150
+ | `url` | `string` | ❌ | GraphQL endpoint URL (default: '/graphql/') |
151
+ | `graphiql` | `boolean` | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |
152
+
153
+ **Example with files:**
154
+
155
+ ```ts
156
+ export default defineNuxtConfig({
157
+ grafserv: {
158
+ type: 'schema',
79
159
  schema: 'server/**/*.graphql',
80
160
  resolvers: 'server/resolvers.ts',
161
+ url: '/graphql',
162
+ }
163
+ })
164
+ ```
81
165
 
82
- // Endpoints
83
- url: '/graphql/', // Serves both GraphQL API and Ruru UI
84
- graphiql: true,
166
+ **Example with schema provider function:**
85
167
 
86
- // Graphile preset with grafserv options
87
- preset: {
88
- grafserv: {
89
- websockets: false,
90
- graphqlOverGET: true, // Enable GET requests for queries
91
- maxRequestLength: 100000,
92
- allowedRequestContentTypes: [
93
- 'application/json',
94
- 'application/graphql+json'
95
- ]
96
- },
97
- grafast: {
98
- explain: true, // Enable plan diagrams
99
- }
168
+ ```ts
169
+ export default defineNuxtConfig({
170
+ grafserv: {
171
+ type: 'schema',
172
+ schema: async () => {
173
+ // Return a GraphQLSchema instance
174
+ return myCustomSchema
100
175
  },
101
176
  }
102
177
  })
103
178
  ```
104
179
 
180
+ ### Configuration Comparison
181
+
182
+ | Feature | PostGraphile Config | Schema Config |
183
+ |---------|---------------------|---------------|
184
+ | Type discriminator | `type: 'postgraphile'` | `type: 'schema'` |
185
+ | Schema source | Generated from preset | Files or function |
186
+ | Resolvers | Auto-generated by PostGraphile | Must provide via resolvers file |
187
+ | Primary use case | PostgreSQL-backed APIs | Custom GraphQL APIs |
188
+ | Setup complexity | Minimal (DB connection only) | Moderate (schema + resolvers) |
189
+ | Plugin system | PostGraphile plugins | Grafast standard steps |
190
+
105
191
  ## Basic Usage
106
192
 
193
+ ### Schema Configuration Example
194
+
195
+ For custom schemas with resolvers:
196
+
107
197
  1. Create your GraphQL schema (`server/schema.graphql`):
108
198
 
109
199
  ```graphql
@@ -120,7 +210,7 @@ type Mutation {
120
210
  2. Create your resolvers (`server/resolvers.ts`):
121
211
 
122
212
  ```typescript
123
- import { constant, lambda, access, object, filter, type GrafastSchemaConfig } from 'grafast'
213
+ import { constant, lambda, type GrafastSchemaConfig } from 'grafast'
124
214
 
125
215
  const resolvers: GrafastSchemaConfig['objects'] = {
126
216
  Query: {
@@ -142,18 +232,68 @@ const resolvers: GrafastSchemaConfig['objects'] = {
142
232
  export default resolvers
143
233
  ```
144
234
 
235
+ 3. Configure Nuxt:
236
+
237
+ ```ts
238
+ export default defineNuxtConfig({
239
+ modules: ['@stonecrop/nuxt-grafserv'],
240
+ grafserv: {
241
+ type: 'schema',
242
+ schema: 'server/schema.graphql',
243
+ resolvers: 'server/resolvers.ts',
244
+ }
245
+ })
246
+ ```
247
+
248
+ ### PostGraphile Configuration Example
249
+
250
+ For database-backed GraphQL APIs:
251
+
252
+ 1. Configure PostGraphile in `nuxt.config.ts`:
253
+
254
+ ```ts
255
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
256
+ import { makePgService } from 'postgraphile/adaptors/pg'
257
+
258
+ export default defineNuxtConfig({
259
+ modules: ['@stonecrop/nuxt-grafserv'],
260
+ grafserv: {
261
+ type: 'postgraphile',
262
+ preset: {
263
+ extends: [PostGraphileAmberPreset],
264
+ pgServices: [
265
+ makePgService({
266
+ connectionString: process.env.DATABASE_URL,
267
+ schemas: ['public'],
268
+ }),
269
+ ],
270
+ },
271
+ }
272
+ })
273
+ ```
274
+
275
+ 2. That's it! PostGraphile automatically generates your GraphQL schema from your PostgreSQL database.
276
+
277
+ ## Advanced Usage
278
+
145
279
  ## Advanced Usage
146
280
 
147
- ### Middleware via Grafserv Plugins
281
+ ### Grafserv Middleware and Plugins
148
282
 
149
- For middleware functionality (authentication, logging, etc.), use Grafserv plugins. The CLI installer creates a `server/plugins.ts` file with examples.
283
+ For cross-cutting concerns like authentication, logging, or rate limiting, use Grafserv plugins through the preset configuration. This works for both PostGraphile and Schema configurations.
150
284
 
151
- #### Inline Plugin Configuration
285
+ #### Inline Plugin Configuration (PostGraphile)
152
286
 
153
287
  ```ts
288
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
289
+ import { makePgService } from 'postgraphile/adaptors/pg'
290
+
154
291
  export default defineNuxtConfig({
155
292
  grafserv: {
293
+ type: 'postgraphile',
156
294
  preset: {
295
+ extends: [PostGraphileAmberPreset],
296
+ pgServices: [makePgService({ /* ... */ })],
157
297
  plugins: [
158
298
  {
159
299
  name: 'request-logging',
@@ -163,9 +303,7 @@ export default defineNuxtConfig({
163
303
  processGraphQLRequestBody: async (next, event) => {
164
304
  const start = Date.now()
165
305
  console.log('[GraphQL] Request started')
166
-
167
306
  const result = await next()
168
-
169
307
  console.log(`[GraphQL] Completed in ${Date.now() - start}ms`)
170
308
  return result
171
309
  }
@@ -178,17 +316,34 @@ export default defineNuxtConfig({
178
316
  })
179
317
  ```
180
318
 
319
+ #### Inline Plugin Configuration (Schema)
320
+
321
+ ```ts
322
+ export default defineNuxtConfig({
323
+ grafserv: {
324
+ type: 'schema',
325
+ schema: 'server/**/*.graphql',
326
+ resolvers: 'server/resolvers.ts',
327
+ // Note: For Schema config, advanced plugin configuration should be done
328
+ // through a preset configuration or custom schema provider
329
+ }
330
+ })
331
+ ```
332
+
181
333
  #### Using External Plugin File
182
334
 
183
335
  ```ts
184
336
  // nuxt.config.ts
337
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
338
+ import { makePgService } from 'postgraphile/adaptors/pg'
185
339
  import plugins from './server/plugins'
186
340
 
187
341
  export default defineNuxtConfig({
188
342
  grafserv: {
189
- schema: 'server/schema.graphql',
190
- resolvers: 'server/resolvers.ts',
343
+ type: 'postgraphile',
191
344
  preset: {
345
+ extends: [PostGraphileAmberPreset],
346
+ pgServices: [makePgService({ /* ... */ })],
192
347
  plugins
193
348
  }
194
349
  }
@@ -219,7 +374,10 @@ const authPlugin: GraphileConfig.Plugin = {
219
374
  middleware: {
220
375
  processGraphQLRequestBody: async (next, event) => {
221
376
  const token = event.request.headers.get('authorization')
222
- // TODO: Validate token and add user to context
377
+ // Validate token and add user to context
378
+ if (!token) {
379
+ throw new Error('Unauthorized')
380
+ }
223
381
  return next()
224
382
  }
225
383
  }
@@ -236,38 +394,128 @@ export default [loggingPlugin, authPlugin]
236
394
  - `ruruHTML` - Customize Ruru IDE HTML generation
237
395
  - `onSubscribe` - Handle GraphQL subscriptions
238
396
 
239
- ## Advanced Usage
397
+ ## PostGraphile Integration
240
398
 
241
- ### Custom Graphile Preset
399
+ This module has first-class support for PostGraphile v5 using the preset configuration pattern. PostGraphile automatically generates your complete GraphQL schema and resolvers from your PostgreSQL database.
242
400
 
243
- Leverage the full power of the Graphile ecosystem with custom presets:
401
+ ### Prerequisites
244
402
 
245
- ```ts
403
+ ```bash
404
+ pnpm add postgraphile
405
+ ```
406
+
407
+ ### Basic PostGraphile Setup
408
+
409
+ ```typescript
410
+ // nuxt.config.ts
246
411
  import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
412
+ import { makePgService } from 'postgraphile/adaptors/pg'
247
413
 
248
414
  export default defineNuxtConfig({
415
+ modules: ['@stonecrop/nuxt-grafserv'],
249
416
  grafserv: {
417
+ type: 'postgraphile',
250
418
  preset: {
251
419
  extends: [PostGraphileAmberPreset],
420
+ pgServices: [
421
+ makePgService({
422
+ connectionString: process.env.DATABASE_URL || 'postgresql://localhost/mydb',
423
+ schemas: ['public'],
424
+ }),
425
+ ],
426
+ },
427
+ url: '/graphql',
428
+ graphiql: true,
429
+ }
430
+ })
431
+ ```
432
+
433
+ ### PostGraphile with Custom Plugins
434
+
435
+ Enhance your PostGraphile setup with community plugins:
436
+
437
+ ```typescript
438
+ // nuxt.config.ts
439
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
440
+ import { makePgService } from 'postgraphile/adaptors/pg'
441
+ import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'
442
+
443
+ export default defineNuxtConfig({
444
+ grafserv: {
445
+ type: 'postgraphile',
446
+ preset: {
447
+ extends: [PostGraphileAmberPreset],
448
+ plugins: [PgSimplifyInflectorPlugin],
449
+ pgServices: [
450
+ makePgService({
451
+ connectionString: process.env.DATABASE_URL,
452
+ schemas: ['public'],
453
+ }),
454
+ ],
455
+ schema: {
456
+ defaultBehavior: 'connection', // Enable Relay-style connections
457
+ },
252
458
  grafast: {
253
- explain: true, // Enable plan diagrams for debugging
254
- context: {
255
- // Custom context available in all resolvers
256
- apiVersion: '1.0'
257
- }
459
+ explain: process.env.NODE_ENV === 'development', // Plan diagrams in dev
258
460
  },
259
- grafserv: {
260
- graphqlOverGET: true,
261
- maxRequestLength: 200000
262
- }
263
- }
461
+ },
264
462
  }
265
463
  })
266
464
  ```
267
465
 
466
+ ### Advanced PostGraphile Configuration
467
+
468
+ ```typescript
469
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
470
+ import { makePgService } from 'postgraphile/adaptors/pg'
471
+
472
+ export default defineNuxtConfig({
473
+ grafserv: {
474
+ type: 'postgraphile',
475
+ preset: {
476
+ extends: [PostGraphileAmberPreset],
477
+ pgServices: [
478
+ makePgService({
479
+ connectionString: process.env.DATABASE_URL,
480
+ schemas: ['public', 'app_private'],
481
+ superuserConnectionString: process.env.SUPERUSER_DATABASE_URL, // For watch mode
482
+ pubsub: true, // Enable LISTEN/NOTIFY for subscriptions
483
+ }),
484
+ ],
485
+ gather: {
486
+ // Smart tags for schema customization
487
+ pgJwtTypes: 'app_public.jwt_token',
488
+ },
489
+ schema: {
490
+ // Behavior overrides
491
+ defaultBehavior: '-insert -update -delete', // Read-only by default
492
+ pgJwtSecret: process.env.JWT_SECRET,
493
+ },
494
+ grafast: {
495
+ explain: true,
496
+ context: (requestContext) => ({
497
+ // Custom context for all resolvers
498
+ userId: requestContext.user?.id,
499
+ }),
500
+ },
501
+ },
502
+ }
503
+ })
504
+ ```
505
+
506
+ ### Benefits of PostGraphile Integration
507
+
508
+ - **Zero Schema Definition**: Automatically generates GraphQL schema from PostgreSQL
509
+ - **Auto-Generated Resolvers**: All queries, mutations, and subscriptions from database
510
+ - **Real-time Subscriptions**: Live queries via PostgreSQL LISTEN/NOTIFY
511
+ - **Row-Level Security**: Leverages PostgreSQL RLS for fine-grained authorization
512
+ - **High Performance**: Grafast execution engine with intelligent query planning
513
+ - **Type Safety**: Full TypeScript support for generated schema
514
+ - **Plugin Ecosystem**: Rich collection of community plugins for extended functionality
515
+
268
516
  ### Accessing Grafserv Instance
269
517
 
270
- For advanced use cases, you can access the grafserv instance directly:
518
+ For advanced use cases, you can access the grafserv instance directly in server routes:
271
519
 
272
520
  ```ts
273
521
  // server/api/custom.ts
@@ -278,10 +526,13 @@ export default defineEventHandler(async (event) => {
278
526
  const config = useRuntimeConfig()
279
527
  const serv = await getGrafservInstance(config.grafserv)
280
528
 
281
- // Access grafserv methods or configuration
282
- const preset = serv.getPreset()
529
+ // Access grafserv methods
530
+ // Note: serv contains the grafserv instance with your schema
283
531
 
284
- return { preset }
532
+ return {
533
+ status: 'GraphQL server is running',
534
+ endpoint: config.grafserv.url
535
+ }
285
536
  })
286
537
  ```
287
538
 
package/dist/module.d.mts CHANGED
@@ -7,22 +7,110 @@ import { GraphileConfig } from 'graphile-config';
7
7
  */
8
8
  type SchemaProvider = () => GraphQLSchema | Promise<GraphQLSchema>;
9
9
  /**
10
- * Configuration for the Grafast module
10
+ * PostGraphile configuration using preset
11
+ *
12
+ * This is the recommended approach for PostGraphile integration.
13
+ * The preset is passed to PostGraphile's makeSchema() function to generate the GraphQL schema.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
18
+ * import { makePgService } from 'postgraphile/adaptors/pg'
19
+ *
20
+ * export default defineNuxtConfig({
21
+ * modules: ['@stonecrop/nuxt-grafserv'],
22
+ * grafserv: {
23
+ * type: 'postgraphile',
24
+ * preset: {
25
+ * extends: [PostGraphileAmberPreset],
26
+ * pgServices: [
27
+ * makePgService({
28
+ * connectionString: process.env.DATABASE_URL,
29
+ * schemas: ['public'],
30
+ * }),
31
+ * ],
32
+ * plugins: [MyCustomPlugin],
33
+ * },
34
+ * url: '/graphql',
35
+ * graphiql: true,
36
+ * },
37
+ * })
38
+ * ```
11
39
  */
12
- interface ModuleOptions {
13
- /** Path to schema file(s) or a schema provider function */
14
- schema?: string | string[] | SchemaProvider;
15
- /** Path to resolvers file (for .graphql schema files) */
40
+ interface PostGraphileConfig {
41
+ /** Configuration type discriminator */
42
+ type: 'postgraphile';
43
+ /** PostGraphile preset configuration - passed to makeSchema() */
44
+ preset: GraphileConfig.Preset;
45
+ /** GraphQL endpoint URL (default: '/graphql/') */
46
+ url?: string;
47
+ /** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
48
+ graphiql?: boolean;
49
+ }
50
+ /**
51
+ * Schema configuration using GraphQL schema files or provider function
52
+ *
53
+ * Use this for custom GraphQL schemas with Grafast resolvers.
54
+ * Schema files (.graphql) are loaded and combined with resolver functions.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Using schema files with resolvers
59
+ * export default defineNuxtConfig({
60
+ * modules: ['@stonecrop/nuxt-grafserv'],
61
+ * grafserv: {
62
+ * type: 'schema',
63
+ * schema: 'server/schema/**\/*.graphql',
64
+ * resolvers: 'server/resolvers/index.ts',
65
+ * url: '/graphql',
66
+ * graphiql: true,
67
+ * },
68
+ * })
69
+ *
70
+ * // Using schema provider function
71
+ * export default defineNuxtConfig({
72
+ * grafserv: {
73
+ * type: 'schema',
74
+ * schema: async () => {
75
+ * // Return a GraphQLSchema instance
76
+ * return myCustomSchema
77
+ * },
78
+ * },
79
+ * })
80
+ * ```
81
+ */
82
+ interface SchemaConfig {
83
+ /** Configuration type discriminator */
84
+ type: 'schema';
85
+ /**
86
+ * Path to schema file(s) or schema provider function
87
+ * - String: Single file path (e.g., 'server/schema/schema.graphql')
88
+ * - Array: Multiple file paths (e.g., ['server/schema/**\/*.graphql'])
89
+ * - Function: Returns a GraphQL schema directly
90
+ */
91
+ schema: string | string[] | SchemaProvider;
92
+ /**
93
+ * Path to resolvers file (optional)
94
+ * Only needed when using .graphql schema files.
95
+ * Should export Grafast resolver objects.
96
+ *
97
+ * @example 'server/resolvers/index.ts'
98
+ */
16
99
  resolvers?: string;
17
100
  /** GraphQL endpoint URL (default: '/graphql/') */
18
101
  url?: string;
19
102
  /** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
20
103
  graphiql?: boolean;
21
- /** Custom Graphile preset to extend (for advanced grafast configuration) */
22
- preset?: GraphileConfig.Preset;
23
104
  }
105
+ /**
106
+ * Module configuration - use either PostGraphile preset or custom schema
107
+ *
108
+ * @see PostGraphileConfig for PostGraphile integration
109
+ * @see SchemaConfig for custom schemas with resolvers
110
+ */
111
+ type ModuleOptions = PostGraphileConfig | SchemaConfig;
24
112
 
25
113
  declare const module$1: NuxtModule<ModuleOptions>;
26
114
 
27
115
  export { module$1 as default };
28
- export type { ModuleOptions, SchemaProvider };
116
+ export type { ModuleOptions, PostGraphileConfig, SchemaConfig, SchemaProvider };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stonecrop/nuxt-grafserv",
3
3
  "configKey": "grafserv",
4
- "version": "0.7.5",
4
+ "version": "0.7.8",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,27 +1,59 @@
1
1
  import { join } from 'node:path';
2
2
  import { createRequire } from 'node:module';
3
- import { useLogger, defineNuxtModule, createResolver } from '@nuxt/kit';
3
+ import { useLogger, defineNuxtModule, createResolver, addTemplate } from '@nuxt/kit';
4
4
 
5
5
  const logger = useLogger("@stonecrop/nuxt-grafserv");
6
+ function validateConfig(options) {
7
+ if (!options.type) {
8
+ throw new Error(
9
+ `[@stonecrop/nuxt-grafserv] Configuration error: 'type' field is required. Must be either 'postgraphile' or 'schema'.
10
+ Example: grafserv: { type: 'postgraphile', preset: { ... } }`
11
+ );
12
+ }
13
+ if (options.type === "postgraphile") {
14
+ const config = options;
15
+ if (!config.preset) {
16
+ throw new Error(
17
+ `[@stonecrop/nuxt-grafserv] PostGraphile configuration error: 'preset' field is required when type is 'postgraphile'.
18
+ Example: { type: 'postgraphile', preset: { extends: [PostGraphileAmberPreset], ... } }`
19
+ );
20
+ }
21
+ if ("schema" in config || "resolvers" in config) {
22
+ logger.warn(
23
+ `PostGraphile configuration should not include 'schema' or 'resolvers' fields. The schema is generated from the 'preset' configuration.`
24
+ );
25
+ }
26
+ } else if (options.type === "schema") {
27
+ const config = options;
28
+ if (!config.schema) {
29
+ throw new Error(
30
+ `[@stonecrop/nuxt-grafserv] Schema configuration error: 'schema' field is required when type is 'schema'.
31
+ Example: { type: 'schema', schema: 'server/**/*.graphql', resolvers: 'server/resolvers/index.ts' }`
32
+ );
33
+ }
34
+ if ("preset" in config) {
35
+ logger.warn(
36
+ `Schema configuration should not include 'preset' field. Use type: 'postgraphile' for PostGraphile preset configuration.`
37
+ );
38
+ }
39
+ } else {
40
+ throw new Error(
41
+ `[@stonecrop/nuxt-grafserv] Configuration error: Invalid type '${options.type}'. Must be either 'postgraphile' or 'schema'.`
42
+ );
43
+ }
44
+ }
6
45
  const module$1 = defineNuxtModule({
7
46
  meta: {
8
47
  name: "@stonecrop/nuxt-grafserv",
9
48
  configKey: "grafserv"
10
49
  },
11
- defaults: (_nuxt) => ({
12
- schema: "server/**/*.graphql",
13
- resolvers: "server/resolvers.ts",
14
- url: "/graphql/",
15
- graphiql: void 0,
16
- // Will default based on dev mode
17
- plugins: [],
18
- preset: {
19
- grafserv: {
20
- websockets: false
21
- }
22
- }
23
- }),
50
+ // No defaults - user must provide complete config with type discriminator
51
+ // Use undefined instead of {} to ensure user config is passed as-is
24
52
  setup(options, nuxt) {
53
+ if (Object.keys(options).length === 0) {
54
+ return;
55
+ }
56
+ validateConfig(options);
25
57
  const { resolve } = createResolver(import.meta.url);
26
58
  const require = createRequire(import.meta.url);
27
59
  nuxt.hook("nitro:config", (config) => {
@@ -43,20 +75,55 @@ const module$1 = defineNuxtModule({
43
75
  logger.info(`Nuxt srcDir: ${nuxt.options.srcDir}`);
44
76
  logger.info(`Nuxt rootDir: ${nuxt.options.rootDir}`);
45
77
  logger.info(`Grafserv server alias: ${config.alias["#grafserv-server"]}`);
46
- const resolverPath = options.resolvers ? resolveForVirtualModule(options.resolvers) : void 0;
47
- logger.info(`Resolved resolver path: ${resolverPath}`);
48
- config.runtimeConfig = config.runtimeConfig || {};
49
- config.runtimeConfig.grafserv = {
50
- ...options,
51
- // Pass resolved resolver path for direct import
52
- resolversPath: resolverPath,
53
- // Resolve schema paths from project root
54
- schema: typeof options.schema === "string" ? resolveForSchema(options.schema) : Array.isArray(options.schema) ? options.schema.map((s) => resolveForSchema(s)) : options.schema
55
- // function passed through
56
- };
57
- config.virtual = config.virtual || {};
58
- if (resolverPath) {
59
- config.virtual["#internal/grafserv/resolvers"] = `export { default } from '${resolverPath}'`;
78
+ if (options.type === "postgraphile") {
79
+ logger.info("Using PostGraphile preset configuration");
80
+ const template = addTemplate({
81
+ filename: "grafserv-preset.mjs",
82
+ write: true,
83
+ getContents: () => {
84
+ return `export const preset = ${JSON.stringify(options.preset, null, 2)}`;
85
+ }
86
+ });
87
+ config.runtimeConfig = config.runtimeConfig || {};
88
+ config.runtimeConfig.grafserv = {
89
+ type: "postgraphile",
90
+ url: options.url || "/graphql/",
91
+ graphiql: options.graphiql,
92
+ presetPath: template.dst
93
+ };
94
+ } else if (options.type === "schema") {
95
+ logger.info("Using schema configuration");
96
+ const resolverPath = options.resolvers ? resolveForVirtualModule(options.resolvers) : void 0;
97
+ if (resolverPath) {
98
+ logger.info(`Resolved resolver path: ${resolverPath}`);
99
+ } else {
100
+ logger.info("No resolvers configured");
101
+ }
102
+ let runtimeSchema;
103
+ if (typeof options.schema === "function") {
104
+ runtimeSchema = options.schema;
105
+ logger.info("Using schema provider function");
106
+ } else if (typeof options.schema === "string") {
107
+ runtimeSchema = resolveForSchema(options.schema);
108
+ logger.info(`Resolved schema path: ${runtimeSchema}`);
109
+ } else if (Array.isArray(options.schema)) {
110
+ runtimeSchema = options.schema.map((s) => resolveForSchema(s));
111
+ logger.info(`Resolved schema paths: ${runtimeSchema.join(", ")}`);
112
+ } else {
113
+ runtimeSchema = options.schema;
114
+ }
115
+ config.runtimeConfig = config.runtimeConfig || {};
116
+ config.runtimeConfig.grafserv = {
117
+ type: "schema",
118
+ schema: runtimeSchema,
119
+ resolversPath: resolverPath,
120
+ url: options.url || "/graphql/",
121
+ graphiql: options.graphiql
122
+ };
123
+ config.virtual = config.virtual || {};
124
+ if (resolverPath) {
125
+ config.virtual["#internal/grafserv/resolvers"] = `export { default } from '${resolverPath}'`;
126
+ }
60
127
  }
61
128
  config.externals = config.externals || {};
62
129
  config.externals.external = config.externals.external || [];
@@ -102,7 +169,7 @@ const module$1 = defineNuxtModule({
102
169
  let cacheClearing = false;
103
170
  nuxt.hook("builder:watch", async (event, path) => {
104
171
  const isSchemaFile = path.endsWith(".graphql");
105
- const isResolverFile = options.resolvers && path.includes(options.resolvers.replace("./", ""));
172
+ const isResolverFile = options.type === "schema" && options.resolvers && path.includes(options.resolvers.replace("./", ""));
106
173
  if (isSchemaFile || isResolverFile) {
107
174
  if (!cacheClearing) {
108
175
  cacheClearing = true;
@@ -18,41 +18,69 @@ async function getSchema(options) {
18
18
  return cachedSchema;
19
19
  }
20
20
  let schema;
21
- if (typeof options.schema === "function") {
22
- schema = await options.schema();
23
- } else if (options.schema) {
24
- const typeDefDocs = await loadTypeDefsFromFiles(options.schema);
25
- const objects = {};
26
- if (options.resolvers) {
27
- try {
28
- const resolverModule = await import("#internal/grafserv/resolvers");
29
- const resolvers = resolverModule.default || resolverModule;
30
- console.debug("[@stonecrop/nuxt-grafserv] Resolvers loaded:", Object.keys(resolvers));
31
- for (const typeName of Object.keys(resolvers)) {
32
- const typeResolvers = resolvers[typeName];
33
- if (typeResolvers && typeof typeResolvers === "object" && "plans" in typeResolvers) {
34
- objects[typeName] = typeResolvers;
35
- } else {
36
- objects[typeName] = { plans: typeResolvers };
37
- }
38
- }
39
- } catch (e) {
40
- console.error("[@stonecrop/nuxt-grafserv] Error loading resolvers:", e);
41
- console.warn("[@stonecrop/nuxt-grafserv] Could not load resolvers:", e);
42
- }
43
- }
21
+ if (options.type === "postgraphile") {
22
+ console.debug("[@stonecrop/nuxt-grafserv] Creating schema from PostGraphile preset");
44
23
  try {
45
- schema = makeGrafastSchema({
46
- typeDefs: typeDefDocs,
47
- objects
48
- });
49
- console.debug("[@stonecrop/nuxt-grafserv] Grafast schema created successfully");
24
+ const { makeSchema } = await import("postgraphile");
25
+ const config = useRuntimeConfig();
26
+ if (config.grafserv.type !== "postgraphile") {
27
+ throw new Error("Expected PostGraphile configuration");
28
+ }
29
+ const presetModule = await import(config.grafserv.presetPath);
30
+ const preset = presetModule.preset;
31
+ const result = await makeSchema(preset);
32
+ schema = result.schema;
33
+ console.debug("[@stonecrop/nuxt-grafserv] PostGraphile schema created successfully");
50
34
  } catch (error) {
51
- console.error("[@stonecrop/nuxt-grafserv] Error creating Grafast schema:", error);
35
+ if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") {
36
+ throw new Error(
37
+ '[@stonecrop/nuxt-grafserv] PostGraphile preset provided but "postgraphile" package not found. Install it with: npm install postgraphile'
38
+ );
39
+ }
40
+ console.error("[@stonecrop/nuxt-grafserv] Error creating PostGraphile schema:", error);
52
41
  throw error;
53
42
  }
43
+ } else if (options.type === "schema") {
44
+ if (typeof options.schema === "function") {
45
+ console.debug("[@stonecrop/nuxt-grafserv] Using schema provider function");
46
+ schema = await options.schema();
47
+ } else {
48
+ console.debug("[@stonecrop/nuxt-grafserv] Loading schema from file(s)");
49
+ const typeDefDocs = await loadTypeDefsFromFiles(options.schema);
50
+ const objects = {};
51
+ if (options.resolversPath) {
52
+ try {
53
+ const resolverModule = await import("#internal/grafserv/resolvers");
54
+ const resolvers = resolverModule.default || resolverModule;
55
+ console.debug("[@stonecrop/nuxt-grafserv] Resolvers loaded:", Object.keys(resolvers));
56
+ for (const typeName of Object.keys(resolvers)) {
57
+ const typeResolvers = resolvers[typeName];
58
+ if (typeResolvers && typeof typeResolvers === "object" && "plans" in typeResolvers) {
59
+ objects[typeName] = typeResolvers;
60
+ } else {
61
+ objects[typeName] = { plans: typeResolvers };
62
+ }
63
+ }
64
+ } catch (e) {
65
+ console.error("[@stonecrop/nuxt-grafserv] Error loading resolvers:", e);
66
+ throw e;
67
+ }
68
+ } else {
69
+ console.debug("[@stonecrop/nuxt-grafserv] No resolvers specified");
70
+ }
71
+ try {
72
+ schema = makeGrafastSchema({
73
+ typeDefs: typeDefDocs,
74
+ objects
75
+ });
76
+ console.debug("[@stonecrop/nuxt-grafserv] Grafast schema created successfully");
77
+ } catch (error) {
78
+ console.error("[@stonecrop/nuxt-grafserv] Error creating Grafast schema:", error);
79
+ throw error;
80
+ }
81
+ }
54
82
  } else {
55
- throw new Error("[@stonecrop/nuxt-grafserv] No schema provided. Configure schema path or provider function.");
83
+ throw new Error(`[@stonecrop/nuxt-grafserv] Invalid configuration type: ${options.type}`);
56
84
  }
57
85
  cachedSchema = schema;
58
86
  return schema;
@@ -63,7 +91,7 @@ export async function getGrafservInstance(options) {
63
91
  return grafservInstance;
64
92
  }
65
93
  const schema = await getSchema(options);
66
- grafservInstance = grafserv({ schema, preset: options.preset });
94
+ grafservInstance = grafserv({ schema });
67
95
  console.log("[@stonecrop/nuxt-grafserv] Grafserv instance created");
68
96
  return grafservInstance;
69
97
  }
package/dist/types.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { default } from './module.mjs'
2
2
 
3
- export { type ModuleOptions, type SchemaProvider } from './module.mjs'
3
+ export { type ModuleOptions, type PostGraphileConfig, type SchemaConfig, type SchemaProvider } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonecrop/nuxt-grafserv",
3
- "version": "0.7.5",
3
+ "version": "0.7.8",
4
4
  "description": "Pluggable Grafserv GraphQL server as Nuxt Module",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,12 +42,10 @@
42
42
  "@graphql-tools/wrap": "^11.1.4",
43
43
  "grafast": "^1.0.0-rc.4",
44
44
  "grafserv": "^1.0.0-rc.4",
45
- "graphile-config": "^1.0.0-rc.3",
46
45
  "graphql": "^16.12.0",
47
- "@stonecrop/graphql-middleware": "0.7.5"
46
+ "@stonecrop/graphql-middleware": "0.7.8"
48
47
  },
49
48
  "devDependencies": {
50
- "@casl/ability": "^6.8.0",
51
49
  "@eslint/js": "^9.39.2",
52
50
  "@nuxt/devtools": "^3.1.1",
53
51
  "@nuxt/eslint-config": "^1.12.1",
@@ -63,16 +61,17 @@
63
61
  "eslint": "^9.39.2",
64
62
  "jsdom": "^27.4.0",
65
63
  "nuxt": "^4.2.2",
64
+ "postgraphile": "^5.0.0-rc.4",
66
65
  "typescript": "^5.9.3",
67
66
  "vite": "^7.3.1",
68
67
  "vitest": "^4.0.17",
69
- "vue-tsc": "^3.2.2",
70
- "@stonecrop/casl-middleware": "0.7.5",
71
- "@stonecrop/rockfoil": "0.7.5"
68
+ "vue-tsc": "^3.2.2"
72
69
  },
73
70
  "peerDependencies": {
74
- "@stonecrop/casl-middleware": "0.7.5",
75
- "@stonecrop/rockfoil": "0.7.5"
71
+ "@stonecrop/casl-middleware": "*",
72
+ "@stonecrop/rockfoil": "*",
73
+ "postgraphile": "^5.0.0-rc.4",
74
+ "graphile-config": "^1.0.0-rc.3"
76
75
  },
77
76
  "peerDependenciesMeta": {
78
77
  "@stonecrop/casl-middleware": {
@@ -80,6 +79,12 @@
80
79
  },
81
80
  "@stonecrop/rockfoil": {
82
81
  "optional": true
82
+ },
83
+ "postgraphile": {
84
+ "optional": true
85
+ },
86
+ "graphile-config": {
87
+ "optional": true
83
88
  }
84
89
  },
85
90
  "scripts": {
@@ -94,6 +99,7 @@
94
99
  "test": "vitest run",
95
100
  "test:ci": "vitest run --run",
96
101
  "test:coverage": "vitest run --coverage",
102
+ "test:e2e": "vitest run test/e2e",
97
103
  "test:watch": "vitest watch",
98
104
  "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
99
105
  }