@stonecrop/nuxt-grafserv 0.7.6 → 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:
104
+
105
+ ### PostGraphile Configuration
63
106
 
64
- | Option | Type | Default | Description |
65
- |--------|------|---------|-------------|
66
- | `schema` | `string \| string[] \| SchemaProvider \| PostGraphileInstance` | `'server/**/*.graphql'` | Path(s) to GraphQL schema files, schema provider function, or PostGraphile instance |
67
- | `resolvers` | `string` | `undefined` | Path to resolvers file (optional - not needed for PostGraphile) |
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 |
107
+ Use `type: 'postgraphile'` for PostGraphile-based GraphQL APIs:
71
108
 
72
- ### Full Configuration Example
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,166 +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
240
-
241
- ### PostGraphile Integration
397
+ ## PostGraphile Integration
242
398
 
243
- This module has first-class support for PostGraphile, enabling instant GraphQL APIs from PostgreSQL databases. PostGraphile generates the complete schema and resolvers, so you don't need to define them manually.
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.
244
400
 
245
- #### Setup with PostGraphile
246
-
247
- 1. Install PostGraphile:
401
+ ### Prerequisites
248
402
 
249
403
  ```bash
250
- pnpm add postgraphile @graphile/crystal graphile-build-pg
251
- ```
252
-
253
- 2. Create a PostGraphile schema provider (`server/graphql/schema.ts`):
254
-
255
- ```typescript
256
- import { postgraphile } from 'postgraphile'
257
- import type { GraphQLSchema } from 'graphql'
258
-
259
- // Define your PostGraphile preset
260
- const preset = {
261
- pgServices: [
262
- {
263
- name: 'main',
264
- connectionString: process.env.DATABASE_URL || 'postgres://localhost/mydb',
265
- schemas: ['public'],
266
- },
267
- ],
268
- grafserv: {
269
- websockets: false, // Disable websockets for Nuxt compatibility
270
- },
271
- }
272
-
273
- // Create PostGraphile instance
274
- const pgl = postgraphile(preset)
275
-
276
- // Export schema provider function
277
- export async function createPostGraphileSchema(): Promise<GraphQLSchema> {
278
- const { schema } = await pgl.getSchemaResult()
279
- return schema
280
- }
281
-
282
- // Export the instance if you need direct access
283
- export { pgl }
404
+ pnpm add postgraphile
284
405
  ```
285
406
 
286
- 3. Configure Nuxt to use the PostGraphile schema:
407
+ ### Basic PostGraphile Setup
287
408
 
288
409
  ```typescript
289
410
  // nuxt.config.ts
290
- import { createPostGraphileSchema } from './server/graphql/schema'
411
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
412
+ import { makePgService } from 'postgraphile/adaptors/pg'
291
413
 
292
414
  export default defineNuxtConfig({
293
415
  modules: ['@stonecrop/nuxt-grafserv'],
294
416
  grafserv: {
295
- // Use PostGraphile schema provider function
296
- schema: createPostGraphileSchema,
297
- // No resolvers needed - PostGraphile generates everything
417
+ type: 'postgraphile',
418
+ preset: {
419
+ extends: [PostGraphileAmberPreset],
420
+ pgServices: [
421
+ makePgService({
422
+ connectionString: process.env.DATABASE_URL || 'postgresql://localhost/mydb',
423
+ schemas: ['public'],
424
+ }),
425
+ ],
426
+ },
298
427
  url: '/graphql',
299
- graphiql: true, // Enable Ruru IDE
428
+ graphiql: true,
300
429
  }
301
430
  })
302
431
  ```
303
432
 
304
- #### Direct PostGraphile Instance Usage
433
+ ### PostGraphile with Custom Plugins
305
434
 
306
- You can also pass a PostGraphile instance directly:
435
+ Enhance your PostGraphile setup with community plugins:
307
436
 
308
437
  ```typescript
309
438
  // nuxt.config.ts
310
- import { pgl } from './server/graphql/schema'
439
+ import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
440
+ import { makePgService } from 'postgraphile/adaptors/pg'
441
+ import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'
311
442
 
312
443
  export default defineNuxtConfig({
313
444
  grafserv: {
314
- schema: pgl, // Pass PostGraphile instance directly
315
- url: '/graphql',
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
+ },
458
+ grafast: {
459
+ explain: process.env.NODE_ENV === 'development', // Plan diagrams in dev
460
+ },
461
+ },
316
462
  }
317
463
  })
318
464
  ```
319
465
 
320
- #### PostGraphile with Custom Plugins
321
-
322
- Enhance your PostGraphile setup with custom plugins:
466
+ ### Advanced PostGraphile Configuration
323
467
 
324
468
  ```typescript
325
- // server/graphql/schema.ts
326
- import { postgraphile } from 'postgraphile'
327
- import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
328
- import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'
329
-
330
- const preset = {
331
- extends: [PostGraphileAmberPreset],
332
- plugins: [PgSimplifyInflectorPlugin],
333
- pgServices: [
334
- {
335
- name: 'main',
336
- connectionString: process.env.DATABASE_URL,
337
- schemas: ['public'],
338
- },
339
- ],
340
- grafserv: {
341
- websockets: false,
342
- },
343
- grafast: {
344
- explain: process.env.NODE_ENV === 'development', // Enable plan diagrams in dev
345
- },
346
- schema: {
347
- // Add custom behavior overrides
348
- defaultBehavior: 'connection', // Enable Relay-style connections by default
349
- },
350
- }
351
-
352
- const pgl = postgraphile(preset)
353
-
354
- export async function createPostGraphileSchema() {
355
- const { schema } = await pgl.getSchemaResult()
356
- return schema
357
- }
358
- ```
359
-
360
- #### Benefits of PostGraphile Integration
361
-
362
- - **Zero Schema Definition**: Automatically generates GraphQL schema from PostgreSQL
363
- - **Auto-Generated Resolvers**: All queries and mutations created from database schema
364
- - **Real-time Subscriptions**: Live query support via PostgreSQL LISTEN/NOTIFY
365
- - **Row-Level Security**: Leverages PostgreSQL RLS for authorization
366
- - **High Performance**: Uses Grafast execution engine with intelligent batching
367
- - **Type Safety**: Full TypeScript support for generated schema
368
-
369
- ### Custom Graphile Preset
370
-
371
- Leverage the full power of the Graphile ecosystem with custom presets:
372
-
373
- ```ts
374
469
  import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
470
+ import { makePgService } from 'postgraphile/adaptors/pg'
375
471
 
376
472
  export default defineNuxtConfig({
377
473
  grafserv: {
474
+ type: 'postgraphile',
378
475
  preset: {
379
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
+ },
380
494
  grafast: {
381
- explain: true, // Enable plan diagrams for debugging
382
- context: {
383
- // Custom context available in all resolvers
384
- apiVersion: '1.0'
385
- }
495
+ explain: true,
496
+ context: (requestContext) => ({
497
+ // Custom context for all resolvers
498
+ userId: requestContext.user?.id,
499
+ }),
386
500
  },
387
- grafserv: {
388
- graphqlOverGET: true,
389
- maxRequestLength: 200000
390
- }
391
- }
501
+ },
392
502
  }
393
503
  })
394
504
  ```
395
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
+
396
516
  ### Accessing Grafserv Instance
397
517
 
398
- 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:
399
519
 
400
520
  ```ts
401
521
  // server/api/custom.ts
@@ -406,10 +526,13 @@ export default defineEventHandler(async (event) => {
406
526
  const config = useRuntimeConfig()
407
527
  const serv = await getGrafservInstance(config.grafserv)
408
528
 
409
- // Access grafserv methods or configuration
410
- const preset = serv.getPreset()
529
+ // Access grafserv methods
530
+ // Note: serv contains the grafserv instance with your schema
411
531
 
412
- return { preset }
532
+ return {
533
+ status: 'GraphQL server is running',
534
+ endpoint: config.grafserv.url
535
+ }
413
536
  })
414
537
  ```
415
538
 
package/dist/module.d.mts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { NuxtModule } from '@nuxt/schema';
2
- import { PromiseOrDirect } from 'grafast';
3
2
  import { GraphQLSchema } from 'graphql';
4
3
  import { GraphileConfig } from 'graphile-config';
5
4
 
@@ -8,32 +7,110 @@ import { GraphileConfig } from 'graphile-config';
8
7
  */
9
8
  type SchemaProvider = () => GraphQLSchema | Promise<GraphQLSchema>;
10
9
  /**
11
- * PostGraphile instance interface (minimal type for compatibility)
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
+ * ```
12
39
  */
13
- interface PostGraphileInstance {
14
- getSchema(): PromiseOrDirect<GraphQLSchema>;
15
- getSchemaResult(): PromiseOrDirect<{
16
- schema: GraphQLSchema;
17
- resolvedPreset: GraphileConfig.ResolvedPreset;
18
- }>;
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;
19
49
  }
20
50
  /**
21
- * Configuration for the Grafast module
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
+ * ```
22
81
  */
23
- interface ModuleOptions {
24
- /** Path to schema file(s), a schema provider function, or a PostGraphile instance */
25
- schema?: string | string[] | SchemaProvider | PostGraphileInstance;
26
- /** Path to resolvers file (optional, only needed for .graphql schema files without PostGraphile) */
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
+ */
27
99
  resolvers?: string;
28
100
  /** GraphQL endpoint URL (default: '/graphql/') */
29
101
  url?: string;
30
102
  /** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
31
103
  graphiql?: boolean;
32
- /** Custom Graphile preset to extend (for advanced grafast configuration) */
33
- preset?: GraphileConfig.Preset;
34
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;
35
112
 
36
113
  declare const module$1: NuxtModule<ModuleOptions>;
37
114
 
38
115
  export { module$1 as default };
39
- 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.6",
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,28 +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: void 0,
14
- // Optional - not needed for PostGraphile setups
15
- url: "/graphql/",
16
- graphiql: void 0,
17
- // Will default based on dev mode
18
- plugins: [],
19
- preset: {
20
- grafserv: {
21
- websockets: false
22
- }
23
- }
24
- }),
50
+ // No defaults - user must provide complete config with type discriminator
51
+ // Use undefined instead of {} to ensure user config is passed as-is
25
52
  setup(options, nuxt) {
53
+ if (Object.keys(options).length === 0) {
54
+ return;
55
+ }
56
+ validateConfig(options);
26
57
  const { resolve } = createResolver(import.meta.url);
27
58
  const require = createRequire(import.meta.url);
28
59
  nuxt.hook("nitro:config", (config) => {
@@ -44,36 +75,55 @@ const module$1 = defineNuxtModule({
44
75
  logger.info(`Nuxt srcDir: ${nuxt.options.srcDir}`);
45
76
  logger.info(`Nuxt rootDir: ${nuxt.options.rootDir}`);
46
77
  logger.info(`Grafserv server alias: ${config.alias["#grafserv-server"]}`);
47
- const resolverPath = options.resolvers ? resolveForVirtualModule(options.resolvers) : void 0;
48
- if (resolverPath) {
49
- logger.info(`Resolved resolver path: ${resolverPath}`);
50
- } else {
51
- logger.info("No resolvers configured (normal for PostGraphile setups)");
52
- }
53
- let runtimeSchema;
54
- if (typeof options.schema === "function" || options.schema && typeof options.schema === "object" && "getSchema" in options.schema) {
55
- runtimeSchema = options.schema;
56
- logger.info("Using schema provider function or PostGraphile instance");
57
- } else if (typeof options.schema === "string") {
58
- runtimeSchema = resolveForSchema(options.schema);
59
- logger.info(`Resolved schema path: ${runtimeSchema}`);
60
- } else if (Array.isArray(options.schema)) {
61
- runtimeSchema = options.schema.map((s) => resolveForSchema(s));
62
- logger.info(`Resolved schema paths: ${runtimeSchema.join(", ")}`);
63
- } else {
64
- runtimeSchema = options.schema;
65
- }
66
- config.runtimeConfig = config.runtimeConfig || {};
67
- config.runtimeConfig.grafserv = {
68
- ...options,
69
- // Pass resolved resolver path for direct import
70
- resolversPath: resolverPath,
71
- // Pass schema (either resolved paths or function/instance)
72
- schema: runtimeSchema
73
- };
74
- config.virtual = config.virtual || {};
75
- if (resolverPath) {
76
- 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
+ }
77
127
  }
78
128
  config.externals = config.externals || {};
79
129
  config.externals.external = config.externals.external || [];
@@ -119,7 +169,7 @@ const module$1 = defineNuxtModule({
119
169
  let cacheClearing = false;
120
170
  nuxt.hook("builder:watch", async (event, path) => {
121
171
  const isSchemaFile = path.endsWith(".graphql");
122
- const isResolverFile = options.resolvers && path.includes(options.resolvers.replace("./", ""));
172
+ const isResolverFile = options.type === "schema" && options.resolvers && path.includes(options.resolvers.replace("./", ""));
123
173
  if (isSchemaFile || isResolverFile) {
124
174
  if (!cacheClearing) {
125
175
  cacheClearing = true;
@@ -13,69 +13,74 @@ async function loadTypeDefsFromFiles(schemaPath) {
13
13
  });
14
14
  return sources.map((source) => source.document).filter(Boolean);
15
15
  }
16
- function isPostGraphileInstance(value) {
17
- return value !== null && typeof value === "object" && "getSchema" in value && typeof value.getSchema === "function";
18
- }
19
16
  async function getSchema(options) {
20
17
  if (cachedSchema) {
21
18
  return cachedSchema;
22
19
  }
23
20
  let schema;
24
- if (isPostGraphileInstance(options.schema)) {
25
- console.debug("[@stonecrop/nuxt-grafserv] Using PostGraphile instance for schema");
21
+ if (options.type === "postgraphile") {
22
+ console.debug("[@stonecrop/nuxt-grafserv] Creating schema from PostGraphile preset");
26
23
  try {
27
- if ("getSchemaResult" in options.schema && typeof options.schema.getSchemaResult === "function") {
28
- const result = await options.schema.getSchemaResult();
29
- schema = result.schema;
30
- } else {
31
- schema = await options.schema.getSchema();
24
+ const { makeSchema } = await import("postgraphile");
25
+ const config = useRuntimeConfig();
26
+ if (config.grafserv.type !== "postgraphile") {
27
+ throw new Error("Expected PostGraphile configuration");
32
28
  }
33
- console.debug("[@stonecrop/nuxt-grafserv] PostGraphile schema loaded successfully");
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");
34
34
  } catch (error) {
35
- console.error("[@stonecrop/nuxt-grafserv] Error loading PostGraphile 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);
36
41
  throw error;
37
42
  }
38
- } else if (typeof options.schema === "function") {
39
- schema = await options.schema();
40
- } else if (options.schema) {
41
- const typeDefDocs = await loadTypeDefsFromFiles(options.schema);
42
- const objects = {};
43
- if (options.resolvers) {
44
- try {
45
- const resolverModule = await import("#internal/grafserv/resolvers");
46
- const resolvers = resolverModule.default || resolverModule;
47
- console.debug("[@stonecrop/nuxt-grafserv] Resolvers loaded:", Object.keys(resolvers));
48
- for (const typeName of Object.keys(resolvers)) {
49
- const typeResolvers = resolvers[typeName];
50
- if (typeResolvers && typeof typeResolvers === "object" && "plans" in typeResolvers) {
51
- objects[typeName] = typeResolvers;
52
- } else {
53
- objects[typeName] = { plans: typeResolvers };
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
+ }
54
63
  }
64
+ } catch (e) {
65
+ console.error("[@stonecrop/nuxt-grafserv] Error loading resolvers:", e);
66
+ throw e;
55
67
  }
56
- } catch (e) {
57
- console.error("[@stonecrop/nuxt-grafserv] Error loading resolvers:", e);
58
- console.warn("[@stonecrop/nuxt-grafserv] Continuing without resolvers - this is normal for PostGraphile setups");
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;
59
80
  }
60
- } else {
61
- console.debug(
62
- "[@stonecrop/nuxt-grafserv] No resolvers specified - using schema-only mode (normal for PostGraphile)"
63
- );
64
- }
65
- try {
66
- schema = makeGrafastSchema({
67
- typeDefs: typeDefDocs,
68
- objects
69
- });
70
- console.debug("[@stonecrop/nuxt-grafserv] Grafast schema created successfully");
71
- } catch (error) {
72
- console.error("[@stonecrop/nuxt-grafserv] Error creating Grafast schema:", error);
73
- throw error;
74
81
  }
75
82
  } else {
76
- throw new Error(
77
- "[@stonecrop/nuxt-grafserv] No schema provided. Configure schema path, provider function, or PostGraphile instance."
78
- );
83
+ throw new Error(`[@stonecrop/nuxt-grafserv] Invalid configuration type: ${options.type}`);
79
84
  }
80
85
  cachedSchema = schema;
81
86
  return schema;
@@ -86,7 +91,7 @@ export async function getGrafservInstance(options) {
86
91
  return grafservInstance;
87
92
  }
88
93
  const schema = await getSchema(options);
89
- grafservInstance = grafserv({ schema, preset: options.preset });
94
+ grafservInstance = grafserv({ schema });
90
95
  console.log("[@stonecrop/nuxt-grafserv] Grafserv instance created");
91
96
  return grafservInstance;
92
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.6",
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.6"
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",
@@ -67,13 +65,13 @@
67
65
  "typescript": "^5.9.3",
68
66
  "vite": "^7.3.1",
69
67
  "vitest": "^4.0.17",
70
- "vue-tsc": "^3.2.2",
71
- "@stonecrop/casl-middleware": "0.7.6",
72
- "@stonecrop/rockfoil": "0.7.6"
68
+ "vue-tsc": "^3.2.2"
73
69
  },
74
70
  "peerDependencies": {
75
- "@stonecrop/casl-middleware": "0.7.6",
76
- "@stonecrop/rockfoil": "0.7.6"
71
+ "@stonecrop/casl-middleware": "*",
72
+ "@stonecrop/rockfoil": "*",
73
+ "postgraphile": "^5.0.0-rc.4",
74
+ "graphile-config": "^1.0.0-rc.3"
77
75
  },
78
76
  "peerDependenciesMeta": {
79
77
  "@stonecrop/casl-middleware": {
@@ -81,6 +79,12 @@
81
79
  },
82
80
  "@stonecrop/rockfoil": {
83
81
  "optional": true
82
+ },
83
+ "postgraphile": {
84
+ "optional": true
85
+ },
86
+ "graphile-config": {
87
+ "optional": true
84
88
  }
85
89
  },
86
90
  "scripts": {