@stonecrop/nuxt-grafserv 0.7.6 → 0.7.9
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 +284 -228
- package/dist/module.d.mts +110 -17
- package/dist/module.json +1 -1
- package/dist/module.mjs +142 -47
- package/dist/runtime/handler.js +49 -48
- package/dist/types.d.mts +1 -1
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -31,79 +31,187 @@ The module automatically registers these handlers:
|
|
|
31
31
|
|
|
32
32
|
## Quick Setup
|
|
33
33
|
|
|
34
|
-
|
|
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
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Create your preset file `server/graphile.preset.ts`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
55
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
56
|
+
|
|
57
|
+
const preset = {
|
|
58
|
+
extends: [PostGraphileAmberPreset],
|
|
59
|
+
pgServices: [
|
|
60
|
+
makePgService({
|
|
61
|
+
connectionString: process.env.DATABASE_URL || 'postgresql://localhost/mydb',
|
|
62
|
+
schemas: ['public'],
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default preset
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
3. Configure in `nuxt.config.ts`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
export default defineNuxtConfig({
|
|
74
|
+
modules: ['@stonecrop/nuxt-grafserv'],
|
|
75
|
+
grafserv: {
|
|
76
|
+
type: 'postgraphile', // Required: specify configuration type
|
|
77
|
+
preset: './server/graphile.preset.ts', // Path to preset file
|
|
78
|
+
url: '/graphql',
|
|
79
|
+
graphiql: true,
|
|
80
|
+
}
|
|
81
|
+
})
|
|
45
82
|
```
|
|
46
83
|
|
|
47
|
-
|
|
84
|
+
### Custom Schema Configuration
|
|
85
|
+
|
|
86
|
+
For custom GraphQL schemas with your own resolvers:
|
|
87
|
+
|
|
88
|
+
1. Add dependency:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pnpm add @stonecrop/nuxt-grafserv
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
2. Configure in `nuxt.config.ts`:
|
|
48
95
|
|
|
49
96
|
```ts
|
|
50
97
|
export default defineNuxtConfig({
|
|
51
98
|
modules: ['@stonecrop/nuxt-grafserv'],
|
|
52
99
|
grafserv: {
|
|
100
|
+
type: 'schema', // Required: specify configuration type
|
|
53
101
|
schema: 'server/**/*.graphql',
|
|
54
102
|
resolvers: 'server/resolvers.ts',
|
|
55
|
-
url: '/graphql
|
|
103
|
+
url: '/graphql',
|
|
104
|
+
graphiql: true,
|
|
56
105
|
}
|
|
57
106
|
})
|
|
58
107
|
```
|
|
59
108
|
|
|
60
109
|
## Configuration
|
|
61
110
|
|
|
62
|
-
|
|
111
|
+
The module supports two configuration types using a discriminated union pattern:
|
|
112
|
+
|
|
113
|
+
### PostGraphile Configuration
|
|
114
|
+
|
|
115
|
+
Use `type: 'postgraphile'` for PostGraphile-based GraphQL APIs:
|
|
116
|
+
|
|
117
|
+
| Option | Type | Required | Description |
|
|
118
|
+
|--------|------|----------|-------------|
|
|
119
|
+
| `type` | `'postgraphile'` | ✅ | Configuration type discriminator |
|
|
120
|
+
| `preset` | `string` | ✅ | Path to PostGraphile preset file (e.g., './server/graphile.preset.ts') |
|
|
121
|
+
| `url` | `string` | ❌ | GraphQL endpoint URL (default: '/graphql/') |
|
|
122
|
+
| `graphiql` | `boolean` | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |
|
|
63
123
|
|
|
64
|
-
|
|
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 |
|
|
124
|
+
> **Important:** The preset must be a file path, not an inline object. See "Why File-Based Presets?" section below.
|
|
71
125
|
|
|
72
|
-
|
|
126
|
+
**Example:**
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// nuxt.config.ts
|
|
130
|
+
export default defineNuxtConfig({
|
|
131
|
+
grafserv: {
|
|
132
|
+
type: 'postgraphile',
|
|
133
|
+
preset: './server/graphile.preset.ts',
|
|
134
|
+
url: '/graphql',
|
|
135
|
+
graphiql: true,
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// server/graphile.preset.ts
|
|
142
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
143
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
144
|
+
|
|
145
|
+
const preset = {
|
|
146
|
+
extends: [PostGraphileAmberPreset],
|
|
147
|
+
pgServices: [
|
|
148
|
+
makePgService({
|
|
149
|
+
connectionString: process.env.DATABASE_URL,
|
|
150
|
+
schemas: ['public'],
|
|
151
|
+
}),
|
|
152
|
+
],
|
|
153
|
+
plugins: [MyCustomPlugin],
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default preset
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Schema Configuration
|
|
160
|
+
|
|
161
|
+
Use `type: 'schema'` for custom GraphQL schemas with Grafast resolvers:
|
|
162
|
+
|
|
163
|
+
| Option | Type | Required | Description |
|
|
164
|
+
|--------|------|----------|-------------|
|
|
165
|
+
| `type` | `'schema'` | ✅ | Configuration type discriminator |
|
|
166
|
+
| `schema` | `string \| string[] \| SchemaProvider` | ✅ | Path(s) to .graphql files or schema provider function |
|
|
167
|
+
| `resolvers` | `string` | ❌ | Path to resolvers file (required for .graphql files) |
|
|
168
|
+
| `url` | `string` | ❌ | GraphQL endpoint URL (default: '/graphql/') |
|
|
169
|
+
| `graphiql` | `boolean` | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |
|
|
170
|
+
|
|
171
|
+
**Example with files:**
|
|
73
172
|
|
|
74
173
|
```ts
|
|
75
174
|
export default defineNuxtConfig({
|
|
76
|
-
modules: ['@stonecrop/nuxt-grafserv'],
|
|
77
175
|
grafserv: {
|
|
78
|
-
|
|
176
|
+
type: 'schema',
|
|
79
177
|
schema: 'server/**/*.graphql',
|
|
80
178
|
resolvers: 'server/resolvers.ts',
|
|
179
|
+
url: '/graphql',
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
```
|
|
81
183
|
|
|
82
|
-
|
|
83
|
-
url: '/graphql/', // Serves both GraphQL API and Ruru UI
|
|
84
|
-
graphiql: true,
|
|
184
|
+
**Example with schema provider function:**
|
|
85
185
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
'application/json',
|
|
94
|
-
'application/graphql+json'
|
|
95
|
-
]
|
|
96
|
-
},
|
|
97
|
-
grafast: {
|
|
98
|
-
explain: true, // Enable plan diagrams
|
|
99
|
-
}
|
|
186
|
+
```ts
|
|
187
|
+
export default defineNuxtConfig({
|
|
188
|
+
grafserv: {
|
|
189
|
+
type: 'schema',
|
|
190
|
+
schema: async () => {
|
|
191
|
+
// Return a GraphQLSchema instance
|
|
192
|
+
return myCustomSchema
|
|
100
193
|
},
|
|
101
194
|
}
|
|
102
195
|
})
|
|
103
196
|
```
|
|
104
197
|
|
|
198
|
+
### Configuration Comparison
|
|
199
|
+
|
|
200
|
+
| Feature | PostGraphile Config | Schema Config |
|
|
201
|
+
|---------|---------------------|---------------|
|
|
202
|
+
| Type discriminator | `type: 'postgraphile'` | `type: 'schema'` |
|
|
203
|
+
| Schema source | Generated from preset | Files or function |
|
|
204
|
+
| Resolvers | Auto-generated by PostGraphile | Must provide via resolvers file |
|
|
205
|
+
| Primary use case | PostgreSQL-backed APIs | Custom GraphQL APIs |
|
|
206
|
+
| Setup complexity | Minimal (DB connection only) | Moderate (schema + resolvers) |
|
|
207
|
+
| Plugin system | PostGraphile plugins | Grafast standard steps |
|
|
208
|
+
|
|
105
209
|
## Basic Usage
|
|
106
210
|
|
|
211
|
+
### Schema Configuration Example
|
|
212
|
+
|
|
213
|
+
For custom schemas with resolvers:
|
|
214
|
+
|
|
107
215
|
1. Create your GraphQL schema (`server/schema.graphql`):
|
|
108
216
|
|
|
109
217
|
```graphql
|
|
@@ -120,7 +228,7 @@ type Mutation {
|
|
|
120
228
|
2. Create your resolvers (`server/resolvers.ts`):
|
|
121
229
|
|
|
122
230
|
```typescript
|
|
123
|
-
import { constant, lambda,
|
|
231
|
+
import { constant, lambda, type GrafastSchemaConfig } from 'grafast'
|
|
124
232
|
|
|
125
233
|
const resolvers: GrafastSchemaConfig['objects'] = {
|
|
126
234
|
Query: {
|
|
@@ -142,61 +250,64 @@ const resolvers: GrafastSchemaConfig['objects'] = {
|
|
|
142
250
|
export default resolvers
|
|
143
251
|
```
|
|
144
252
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
### Middleware via Grafserv Plugins
|
|
148
|
-
|
|
149
|
-
For middleware functionality (authentication, logging, etc.), use Grafserv plugins. The CLI installer creates a `server/plugins.ts` file with examples.
|
|
150
|
-
|
|
151
|
-
#### Inline Plugin Configuration
|
|
253
|
+
3. Configure Nuxt:
|
|
152
254
|
|
|
153
255
|
```ts
|
|
154
256
|
export default defineNuxtConfig({
|
|
257
|
+
modules: ['@stonecrop/nuxt-grafserv'],
|
|
155
258
|
grafserv: {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
name: 'request-logging',
|
|
160
|
-
version: '1.0.0',
|
|
161
|
-
grafserv: {
|
|
162
|
-
middleware: {
|
|
163
|
-
processGraphQLRequestBody: async (next, event) => {
|
|
164
|
-
const start = Date.now()
|
|
165
|
-
console.log('[GraphQL] Request started')
|
|
166
|
-
|
|
167
|
-
const result = await next()
|
|
168
|
-
|
|
169
|
-
console.log(`[GraphQL] Completed in ${Date.now() - start}ms`)
|
|
170
|
-
return result
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
]
|
|
176
|
-
}
|
|
259
|
+
type: 'schema',
|
|
260
|
+
schema: 'server/schema.graphql',
|
|
261
|
+
resolvers: 'server/resolvers.ts',
|
|
177
262
|
}
|
|
178
263
|
})
|
|
179
264
|
```
|
|
180
265
|
|
|
181
|
-
|
|
266
|
+
### PostGraphile Configuration Example
|
|
267
|
+
|
|
268
|
+
For database-backed GraphQL APIs:
|
|
269
|
+
|
|
270
|
+
1. Create your preset file `server/graphile.preset.ts`:
|
|
182
271
|
|
|
183
272
|
```ts
|
|
184
|
-
|
|
185
|
-
import
|
|
273
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
274
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
186
275
|
|
|
276
|
+
const preset = {
|
|
277
|
+
extends: [PostGraphileAmberPreset],
|
|
278
|
+
pgServices: [
|
|
279
|
+
makePgService({
|
|
280
|
+
connectionString: process.env.DATABASE_URL,
|
|
281
|
+
schemas: ['public'],
|
|
282
|
+
}),
|
|
283
|
+
],
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export default preset
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
2. Configure PostGraphile in `nuxt.config.ts`:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
187
292
|
export default defineNuxtConfig({
|
|
293
|
+
modules: ['@stonecrop/nuxt-grafserv'],
|
|
188
294
|
grafserv: {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
preset: {
|
|
192
|
-
plugins
|
|
193
|
-
}
|
|
295
|
+
type: 'postgraphile',
|
|
296
|
+
preset: './server/graphile.preset.ts',
|
|
194
297
|
}
|
|
195
298
|
})
|
|
196
299
|
```
|
|
197
300
|
|
|
301
|
+
3. That's it! PostGraphile automatically generates your GraphQL schema from your PostgreSQL database.
|
|
302
|
+
|
|
303
|
+
## Advanced Usage
|
|
304
|
+
|
|
305
|
+
### Custom Grafserv Plugins
|
|
306
|
+
|
|
307
|
+
Add custom plugins for authentication, logging, or rate limiting through your preset file:
|
|
308
|
+
|
|
198
309
|
```ts
|
|
199
|
-
// server/plugins.ts
|
|
310
|
+
// server/graphile/plugins.ts
|
|
200
311
|
import type { GraphileConfig } from 'graphile-config'
|
|
201
312
|
|
|
202
313
|
const loggingPlugin: GraphileConfig.Plugin = {
|
|
@@ -219,7 +330,10 @@ const authPlugin: GraphileConfig.Plugin = {
|
|
|
219
330
|
middleware: {
|
|
220
331
|
processGraphQLRequestBody: async (next, event) => {
|
|
221
332
|
const token = event.request.headers.get('authorization')
|
|
222
|
-
//
|
|
333
|
+
// Validate token and add user to context
|
|
334
|
+
if (!token) {
|
|
335
|
+
throw new Error('Unauthorized')
|
|
336
|
+
}
|
|
223
337
|
return next()
|
|
224
338
|
}
|
|
225
339
|
}
|
|
@@ -229,6 +343,23 @@ const authPlugin: GraphileConfig.Plugin = {
|
|
|
229
343
|
export default [loggingPlugin, authPlugin]
|
|
230
344
|
```
|
|
231
345
|
|
|
346
|
+
Import the plugins in your preset:
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
// server/graphile/graphile.preset.ts
|
|
350
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
351
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
352
|
+
import plugins from './plugins'
|
|
353
|
+
|
|
354
|
+
const preset = {
|
|
355
|
+
extends: [PostGraphileAmberPreset],
|
|
356
|
+
pgServices: [makePgService({ /* ... */ })],
|
|
357
|
+
plugins
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export default preset
|
|
361
|
+
```
|
|
362
|
+
|
|
232
363
|
#### Available Middleware Hooks
|
|
233
364
|
|
|
234
365
|
- `processRequest` - Process all incoming requests
|
|
@@ -236,186 +367,122 @@ export default [loggingPlugin, authPlugin]
|
|
|
236
367
|
- `ruruHTML` - Customize Ruru IDE HTML generation
|
|
237
368
|
- `onSubscribe` - Handle GraphQL subscriptions
|
|
238
369
|
|
|
239
|
-
##
|
|
240
|
-
|
|
241
|
-
### PostGraphile Integration
|
|
242
|
-
|
|
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.
|
|
370
|
+
## PostGraphile Integration
|
|
244
371
|
|
|
245
|
-
|
|
372
|
+
PostGraphile v5+ automatically generates your GraphQL schema from PostgreSQL.
|
|
246
373
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
pnpm add postgraphile @graphile/crystal graphile-build-pg
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
2. Create a PostGraphile schema provider (`server/graphql/schema.ts`):
|
|
374
|
+
### With Community Plugins
|
|
254
375
|
|
|
255
376
|
```typescript
|
|
256
|
-
import {
|
|
257
|
-
import
|
|
377
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
378
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
379
|
+
import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'
|
|
258
380
|
|
|
259
|
-
// Define your PostGraphile preset
|
|
260
381
|
const preset = {
|
|
382
|
+
extends: [PostGraphileAmberPreset],
|
|
383
|
+
plugins: [PgSimplifyInflectorPlugin],
|
|
261
384
|
pgServices: [
|
|
262
|
-
{
|
|
263
|
-
|
|
264
|
-
connectionString: process.env.DATABASE_URL || 'postgres://localhost/mydb',
|
|
385
|
+
makePgService({
|
|
386
|
+
connectionString: process.env.DATABASE_URL,
|
|
265
387
|
schemas: ['public'],
|
|
266
|
-
},
|
|
388
|
+
}),
|
|
267
389
|
],
|
|
268
|
-
|
|
269
|
-
|
|
390
|
+
schema: {
|
|
391
|
+
defaultBehavior: 'connection', // Enable Relay-style connections
|
|
392
|
+
},
|
|
393
|
+
grafast: {
|
|
394
|
+
explain: process.env.NODE_ENV === 'development', // Plan diagrams in dev
|
|
270
395
|
},
|
|
271
396
|
}
|
|
272
397
|
|
|
273
|
-
|
|
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 }
|
|
398
|
+
export default preset
|
|
284
399
|
```
|
|
285
400
|
|
|
286
|
-
|
|
401
|
+
### Advanced Configuration
|
|
287
402
|
|
|
288
403
|
```typescript
|
|
289
|
-
// nuxt.config.ts
|
|
290
|
-
import { createPostGraphileSchema } from './server/graphql/schema'
|
|
291
|
-
|
|
292
|
-
export default defineNuxtConfig({
|
|
293
|
-
modules: ['@stonecrop/nuxt-grafserv'],
|
|
294
|
-
grafserv: {
|
|
295
|
-
// Use PostGraphile schema provider function
|
|
296
|
-
schema: createPostGraphileSchema,
|
|
297
|
-
// No resolvers needed - PostGraphile generates everything
|
|
298
|
-
url: '/graphql',
|
|
299
|
-
graphiql: true, // Enable Ruru IDE
|
|
300
|
-
}
|
|
301
|
-
})
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
#### Direct PostGraphile Instance Usage
|
|
305
|
-
|
|
306
|
-
You can also pass a PostGraphile instance directly:
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
// nuxt.config.ts
|
|
310
|
-
import { pgl } from './server/graphql/schema'
|
|
311
|
-
|
|
312
|
-
export default defineNuxtConfig({
|
|
313
|
-
grafserv: {
|
|
314
|
-
schema: pgl, // Pass PostGraphile instance directly
|
|
315
|
-
url: '/graphql',
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
#### PostGraphile with Custom Plugins
|
|
321
|
-
|
|
322
|
-
Enhance your PostGraphile setup with custom plugins:
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
// server/graphql/schema.ts
|
|
326
|
-
import { postgraphile } from 'postgraphile'
|
|
327
404
|
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
328
|
-
import
|
|
405
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
329
406
|
|
|
330
407
|
const preset = {
|
|
331
408
|
extends: [PostGraphileAmberPreset],
|
|
332
|
-
plugins: [PgSimplifyInflectorPlugin],
|
|
333
409
|
pgServices: [
|
|
334
|
-
{
|
|
335
|
-
name: 'main',
|
|
410
|
+
makePgService({
|
|
336
411
|
connectionString: process.env.DATABASE_URL,
|
|
337
|
-
schemas: ['public'],
|
|
338
|
-
|
|
412
|
+
schemas: ['public', 'app_private'],
|
|
413
|
+
superuserConnectionString: process.env.SUPERUSER_DATABASE_URL, // For watch mode
|
|
414
|
+
pubsub: true, // Enable LISTEN/NOTIFY for subscriptions
|
|
415
|
+
}),
|
|
339
416
|
],
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
grafast: {
|
|
344
|
-
explain: process.env.NODE_ENV === 'development', // Enable plan diagrams in dev
|
|
417
|
+
gather: {
|
|
418
|
+
// Smart tags for schema customization
|
|
419
|
+
pgJwtTypes: 'app_public.jwt_token',
|
|
345
420
|
},
|
|
346
421
|
schema: {
|
|
347
|
-
//
|
|
348
|
-
defaultBehavior: '
|
|
422
|
+
// Behavior overrides
|
|
423
|
+
defaultBehavior: '-insert -update -delete', // Read-only by default
|
|
424
|
+
pgJwtSecret: process.env.JWT_SECRET,
|
|
425
|
+
},
|
|
426
|
+
grafast: {
|
|
427
|
+
explain: true,
|
|
428
|
+
context: (requestContext) => ({
|
|
429
|
+
// Custom context for all resolvers
|
|
430
|
+
userId: requestContext.user?.id,
|
|
431
|
+
}),
|
|
349
432
|
},
|
|
350
433
|
}
|
|
351
434
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
export async function createPostGraphileSchema() {
|
|
355
|
-
const { schema } = await pgl.getSchemaResult()
|
|
356
|
-
return schema
|
|
357
|
-
}
|
|
435
|
+
export default preset
|
|
358
436
|
```
|
|
359
437
|
|
|
360
|
-
|
|
438
|
+
### Why File-Based Presets?
|
|
361
439
|
|
|
362
|
-
|
|
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
|
|
440
|
+
PostGraphile presets must be in separate files, not inline in `nuxt.config.ts`.
|
|
368
441
|
|
|
369
|
-
|
|
442
|
+
The module uses PostGraphile's [instance-based approach](https://postgraphile.org/postgraphile/next/usage-library):
|
|
370
443
|
|
|
371
|
-
|
|
444
|
+
1. At build time, the preset file is imported and a PostGraphile instance is created
|
|
445
|
+
2. The instance is exposed via Nitro's virtual module system
|
|
446
|
+
3. At runtime, handlers call `pgl.getSchema()` to get the schema
|
|
372
447
|
|
|
373
|
-
|
|
448
|
+
This approach avoids GraphQL module duplication, follows PostGraphile's recommended pattern, supports watch mode, and properly handles database connections.
|
|
449
|
+
|
|
450
|
+
**Your preset file needs a default export:**
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
374
453
|
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
375
454
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
grafast: {
|
|
381
|
-
explain: true, // Enable plan diagrams for debugging
|
|
382
|
-
context: {
|
|
383
|
-
// Custom context available in all resolvers
|
|
384
|
-
apiVersion: '1.0'
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
grafserv: {
|
|
388
|
-
graphqlOverGET: true,
|
|
389
|
-
maxRequestLength: 200000
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
})
|
|
394
|
-
```
|
|
455
|
+
const preset = {
|
|
456
|
+
extends: [PostGraphileAmberPreset],
|
|
457
|
+
// ...
|
|
458
|
+
}
|
|
395
459
|
|
|
396
|
-
|
|
460
|
+
export default preset
|
|
461
|
+
```
|
|
397
462
|
|
|
398
|
-
|
|
463
|
+
**Import Requirements:**
|
|
399
464
|
|
|
400
|
-
|
|
401
|
-
// server/api/custom.ts
|
|
402
|
-
import { getGrafservInstance } from '@stonecrop/nuxt-grafserv/runtime/handler'
|
|
403
|
-
import { useRuntimeConfig } from '#imports'
|
|
465
|
+
Relative imports in preset files work with file extensions:
|
|
404
466
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
467
|
+
```typescript
|
|
468
|
+
// server/graphile/graphile.preset.ts
|
|
469
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
470
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
471
|
+
import { MyPlugin } from './plugins/my-plugin'
|
|
472
|
+
import { AnotherPlugin } from './plugins/another'
|
|
408
473
|
|
|
409
|
-
|
|
410
|
-
|
|
474
|
+
const preset = {
|
|
475
|
+
extends: [PostGraphileAmberPreset],
|
|
476
|
+
pgServices: [makePgService({/*...*/})],
|
|
477
|
+
plugins: [MyPlugin, AnotherPlugin],
|
|
478
|
+
}
|
|
411
479
|
|
|
412
|
-
|
|
413
|
-
})
|
|
480
|
+
export default preset
|
|
414
481
|
```
|
|
415
482
|
|
|
416
|
-
### Schema Building with
|
|
483
|
+
### Schema Building with Grafast
|
|
417
484
|
|
|
418
|
-
|
|
485
|
+
Grafast uses a step-based execution model. Common patterns:
|
|
419
486
|
|
|
420
487
|
```typescript
|
|
421
488
|
import { constant, lambda, access, object, filter, type GrafastSchemaConfig } from 'grafast'
|
|
@@ -498,26 +565,15 @@ export default resolvers
|
|
|
498
565
|
```
|
|
499
566
|
|
|
500
567
|
**Key Concepts:**
|
|
501
|
-
-
|
|
502
|
-
- Arguments are accessed via `fieldArgs.$argumentName`
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
-
|
|
506
|
-
-
|
|
507
|
-
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
**Standard Steps Reference:**
|
|
511
|
-
- `constant()` - Create a step from a static value
|
|
512
|
-
- `lambda()` - Transform step values with execution-time callbacks
|
|
513
|
-
- `access()` - Extract properties from object steps
|
|
514
|
-
- `object()` - Compose objects from multiple steps
|
|
515
|
-
- `filter()` - Filter list steps based on conditions
|
|
516
|
-
- `list()` - Create lists from step tuples
|
|
517
|
-
- `first()`/`last()` - Get first/last elements from lists
|
|
518
|
-
- `loadOne()`/`loadMany()` - Batch data loading (DataLoader-style)
|
|
519
|
-
|
|
520
|
-
For the complete list, see [Grafast Standard Steps](https://grafast.org/grafast/standard-steps)
|
|
568
|
+
- Resolvers return **steps** (constant, lambda, access, object, filter), not plain values
|
|
569
|
+
- Arguments are accessed via `fieldArgs.$argumentName`
|
|
570
|
+
- `constant()` - static values
|
|
571
|
+
- `lambda()` - transform step values at execution time
|
|
572
|
+
- `access()` - extract object properties efficiently
|
|
573
|
+
- `object()` - compose objects from steps
|
|
574
|
+
- `filter()` - filter list steps
|
|
575
|
+
|
|
576
|
+
See [Grafast Standard Steps](https://grafast.org/grafast/standard-steps) for the complete reference.
|
|
521
577
|
|
|
522
578
|
## Development
|
|
523
579
|
|
package/dist/module.d.mts
CHANGED
|
@@ -1,39 +1,132 @@
|
|
|
1
1
|
import { NuxtModule } from '@nuxt/schema';
|
|
2
|
-
import { PromiseOrDirect } from 'grafast';
|
|
3
2
|
import { GraphQLSchema } from 'graphql';
|
|
4
|
-
import { GraphileConfig } from 'graphile-config';
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* Schema provider function - returns a GraphQL schema
|
|
8
6
|
*/
|
|
9
7
|
type SchemaProvider = () => GraphQLSchema | Promise<GraphQLSchema>;
|
|
10
8
|
/**
|
|
11
|
-
* PostGraphile
|
|
9
|
+
* PostGraphile configuration using preset
|
|
10
|
+
*
|
|
11
|
+
* This is the recommended approach for PostGraphile integration.
|
|
12
|
+
* The preset file path is resolved and imported at runtime, then passed to PostGraphile's makeSchema() function.
|
|
13
|
+
*
|
|
14
|
+
* Note: Inline preset objects are not supported due to Nitro's build/runtime separation.
|
|
15
|
+
* Complex objects with extends arrays, plugins, and functions cannot be serialized across this boundary.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // 1. Create server/graphile.preset.ts
|
|
20
|
+
* import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
21
|
+
* import { makePgService } from 'postgraphile/adaptors/pg'
|
|
22
|
+
*
|
|
23
|
+
* export default {
|
|
24
|
+
* extends: [PostGraphileAmberPreset],
|
|
25
|
+
* pgServices: [
|
|
26
|
+
* makePgService({
|
|
27
|
+
* connectionString: process.env.DATABASE_URL,
|
|
28
|
+
* schemas: ['public'],
|
|
29
|
+
* }),
|
|
30
|
+
* ],
|
|
31
|
+
* plugins: [MyCustomPlugin],
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* // 2. Reference the preset file in nuxt.config.ts
|
|
35
|
+
* export default defineNuxtConfig({
|
|
36
|
+
* modules: ['@stonecrop/nuxt-grafserv'],
|
|
37
|
+
* grafserv: {
|
|
38
|
+
* type: 'postgraphile',
|
|
39
|
+
* preset: './server/graphile.preset.ts',
|
|
40
|
+
* url: '/graphql',
|
|
41
|
+
* graphiql: true,
|
|
42
|
+
* },
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
12
45
|
*/
|
|
13
|
-
interface
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
46
|
+
interface PostGraphileConfig {
|
|
47
|
+
/** Configuration type discriminator */
|
|
48
|
+
type: 'postgraphile';
|
|
49
|
+
/**
|
|
50
|
+
* Path to PostGraphile preset file
|
|
51
|
+
*
|
|
52
|
+
* The preset file must export the preset configuration using default export:
|
|
53
|
+
* `export default { extends: [...], pgServices: [...], plugins: [...] }`
|
|
54
|
+
*
|
|
55
|
+
* A PostGraphile instance will be created from this preset at build time.
|
|
56
|
+
*
|
|
57
|
+
* @example './server/graphile.preset.ts'
|
|
58
|
+
* @example './server/graphile.preset.js'
|
|
59
|
+
*/
|
|
60
|
+
preset: string;
|
|
61
|
+
/** GraphQL endpoint URL (default: '/graphql/') */
|
|
62
|
+
url?: string;
|
|
63
|
+
/** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
|
|
64
|
+
graphiql?: boolean;
|
|
19
65
|
}
|
|
20
66
|
/**
|
|
21
|
-
*
|
|
67
|
+
* Schema configuration using GraphQL schema files or provider function
|
|
68
|
+
*
|
|
69
|
+
* Use this for custom GraphQL schemas with Grafast resolvers.
|
|
70
|
+
* Schema files (.graphql) are loaded and combined with resolver functions.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Using schema files with resolvers
|
|
75
|
+
* export default defineNuxtConfig({
|
|
76
|
+
* modules: ['@stonecrop/nuxt-grafserv'],
|
|
77
|
+
* grafserv: {
|
|
78
|
+
* type: 'schema',
|
|
79
|
+
* schema: 'server/schema/**\/*.graphql',
|
|
80
|
+
* resolvers: 'server/resolvers/index.ts',
|
|
81
|
+
* url: '/graphql',
|
|
82
|
+
* graphiql: true,
|
|
83
|
+
* },
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* // Using schema provider function
|
|
87
|
+
* export default defineNuxtConfig({
|
|
88
|
+
* grafserv: {
|
|
89
|
+
* type: 'schema',
|
|
90
|
+
* schema: async () => {
|
|
91
|
+
* // Return a GraphQLSchema instance
|
|
92
|
+
* return myCustomSchema
|
|
93
|
+
* },
|
|
94
|
+
* },
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
22
97
|
*/
|
|
23
|
-
interface
|
|
24
|
-
/**
|
|
25
|
-
schema
|
|
26
|
-
/**
|
|
98
|
+
interface SchemaConfig {
|
|
99
|
+
/** Configuration type discriminator */
|
|
100
|
+
type: 'schema';
|
|
101
|
+
/**
|
|
102
|
+
* Path to schema file(s) or schema provider function
|
|
103
|
+
* - String: Single file path (e.g., 'server/schema/schema.graphql')
|
|
104
|
+
* - Array: Multiple file paths (e.g., ['server/schema/**\/*.graphql'])
|
|
105
|
+
* - Function: Returns a GraphQL schema directly
|
|
106
|
+
*/
|
|
107
|
+
schema: string | string[] | SchemaProvider;
|
|
108
|
+
/**
|
|
109
|
+
* Path to resolvers file (optional)
|
|
110
|
+
* Only needed when using .graphql schema files.
|
|
111
|
+
* Should export Grafast resolver objects.
|
|
112
|
+
*
|
|
113
|
+
* @example 'server/resolvers/index.ts'
|
|
114
|
+
*/
|
|
27
115
|
resolvers?: string;
|
|
28
116
|
/** GraphQL endpoint URL (default: '/graphql/') */
|
|
29
117
|
url?: string;
|
|
30
118
|
/** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
|
|
31
119
|
graphiql?: boolean;
|
|
32
|
-
/** Custom Graphile preset to extend (for advanced grafast configuration) */
|
|
33
|
-
preset?: GraphileConfig.Preset;
|
|
34
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Module configuration - use either PostGraphile preset or custom schema
|
|
123
|
+
*
|
|
124
|
+
* @see PostGraphileConfig for PostGraphile integration
|
|
125
|
+
* @see SchemaConfig for custom schemas with resolvers
|
|
126
|
+
*/
|
|
127
|
+
type ModuleOptions = PostGraphileConfig | SchemaConfig;
|
|
35
128
|
|
|
36
129
|
declare const module$1: NuxtModule<ModuleOptions>;
|
|
37
130
|
|
|
38
131
|
export { module$1 as default };
|
|
39
|
-
export type { ModuleOptions, SchemaProvider };
|
|
132
|
+
export type { ModuleOptions, PostGraphileConfig, SchemaConfig, SchemaProvider };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,28 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
+
import { join } from 'node:path';
|
|
3
4
|
import { useLogger, defineNuxtModule, createResolver } from '@nuxt/kit';
|
|
4
5
|
|
|
5
6
|
const logger = useLogger("@stonecrop/nuxt-grafserv");
|
|
7
|
+
function validateConfig(options) {
|
|
8
|
+
if (!options.type) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`[@stonecrop/nuxt-grafserv] Configuration error: 'type' field is required. Must be either 'postgraphile' or 'schema'.
|
|
11
|
+
Example: grafserv: { type: 'postgraphile', preset: { ... } }`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
if (options.type === "postgraphile") {
|
|
15
|
+
const config = options;
|
|
16
|
+
if (!config.preset) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`[@stonecrop/nuxt-grafserv] PostGraphile configuration error: 'preset' field is required when type is 'postgraphile'.
|
|
19
|
+
Example: { type: 'postgraphile', preset: { extends: [PostGraphileAmberPreset], ... } }`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
if ("schema" in config || "resolvers" in config) {
|
|
23
|
+
logger.warn(
|
|
24
|
+
`PostGraphile configuration should not include 'schema' or 'resolvers' fields. The schema is generated from the 'preset' configuration.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
} else if (options.type === "schema") {
|
|
28
|
+
const config = options;
|
|
29
|
+
if (!config.schema) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`[@stonecrop/nuxt-grafserv] Schema configuration error: 'schema' field is required when type is 'schema'.
|
|
32
|
+
Example: { type: 'schema', schema: 'server/**/*.graphql', resolvers: 'server/resolvers/index.ts' }`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if ("preset" in config) {
|
|
36
|
+
logger.warn(
|
|
37
|
+
`Schema configuration should not include 'preset' field. Use type: 'postgraphile' for PostGraphile preset configuration.`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`[@stonecrop/nuxt-grafserv] Configuration error: Invalid type '${options.type}'. Must be either 'postgraphile' or 'schema'.`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
6
46
|
const module$1 = defineNuxtModule({
|
|
7
47
|
meta: {
|
|
8
48
|
name: "@stonecrop/nuxt-grafserv",
|
|
9
49
|
configKey: "grafserv"
|
|
10
50
|
},
|
|
11
|
-
defaults
|
|
12
|
-
|
|
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
|
-
}),
|
|
51
|
+
// No defaults - user must provide complete config with type discriminator
|
|
52
|
+
// Use undefined instead of {} to ensure user config is passed as-is
|
|
25
53
|
setup(options, nuxt) {
|
|
54
|
+
if (Object.keys(options).length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
validateConfig(options);
|
|
26
58
|
const { resolve } = createResolver(import.meta.url);
|
|
27
59
|
const require = createRequire(import.meta.url);
|
|
28
60
|
nuxt.hook("nitro:config", (config) => {
|
|
@@ -44,41 +76,98 @@ const module$1 = defineNuxtModule({
|
|
|
44
76
|
logger.info(`Nuxt srcDir: ${nuxt.options.srcDir}`);
|
|
45
77
|
logger.info(`Nuxt rootDir: ${nuxt.options.rootDir}`);
|
|
46
78
|
logger.info(`Grafserv server alias: ${config.alias["#grafserv-server"]}`);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
if (options.type === "postgraphile") {
|
|
80
|
+
logger.info("Using PostGraphile preset configuration");
|
|
81
|
+
let presetPath = resolveForVirtualModule(options.preset);
|
|
82
|
+
if (!existsSync(presetPath)) {
|
|
83
|
+
const extensions = [".ts", ".js", ".mjs"];
|
|
84
|
+
let found = false;
|
|
85
|
+
for (const ext of extensions) {
|
|
86
|
+
const pathWithExt = presetPath + ext;
|
|
87
|
+
if (existsSync(pathWithExt)) {
|
|
88
|
+
presetPath = pathWithExt;
|
|
89
|
+
found = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!found) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`[@stonecrop/nuxt-grafserv] Preset file not found: ${presetPath}
|
|
96
|
+
Tried extensions: ${extensions.join(", ")}
|
|
97
|
+
|
|
98
|
+
Create the preset file with your PostGraphile configuration:
|
|
99
|
+
|
|
100
|
+
// ${options.preset}.ts (or .js, .mjs)
|
|
101
|
+
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
|
|
102
|
+
import { makePgService } from 'postgraphile/adaptors/pg'
|
|
103
|
+
|
|
104
|
+
export default {
|
|
105
|
+
extends: [PostGraphileAmberPreset],
|
|
106
|
+
pgServices: [makePgService({ connectionString: process.env.DATABASE_URL, schemas: ['public'] })],
|
|
107
|
+
plugins: [],
|
|
108
|
+
}
|
|
109
|
+
`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
logger.info(`Resolved preset path: ${presetPath}`);
|
|
114
|
+
config.virtual = config.virtual || {};
|
|
115
|
+
config.virtual["#internal/grafserv/pgl"] = [
|
|
116
|
+
`import { postgraphile } from 'postgraphile'`,
|
|
117
|
+
`import preset from '${presetPath}'`,
|
|
118
|
+
``,
|
|
119
|
+
`// Create PostGraphile instance with the preset`,
|
|
120
|
+
`// This handles schema building, watch mode, and lifecycle`,
|
|
121
|
+
`export const pgl = postgraphile(preset)`
|
|
122
|
+
].join("\n");
|
|
123
|
+
config.runtimeConfig = config.runtimeConfig || {};
|
|
124
|
+
config.runtimeConfig.grafserv = {
|
|
125
|
+
type: "postgraphile",
|
|
126
|
+
url: options.url || "/graphql/",
|
|
127
|
+
graphiql: options.graphiql
|
|
128
|
+
};
|
|
129
|
+
} else if (options.type === "schema") {
|
|
130
|
+
logger.info("Using schema configuration");
|
|
131
|
+
const resolverPath = options.resolvers ? resolveForVirtualModule(options.resolvers) : void 0;
|
|
132
|
+
if (resolverPath) {
|
|
133
|
+
logger.info(`Resolved resolver path: ${resolverPath}`);
|
|
134
|
+
} else {
|
|
135
|
+
logger.info("No resolvers configured");
|
|
136
|
+
}
|
|
137
|
+
let runtimeSchema;
|
|
138
|
+
if (typeof options.schema === "function") {
|
|
139
|
+
runtimeSchema = options.schema;
|
|
140
|
+
logger.info("Using schema provider function");
|
|
141
|
+
} else if (typeof options.schema === "string") {
|
|
142
|
+
runtimeSchema = resolveForSchema(options.schema);
|
|
143
|
+
logger.info(`Resolved schema path: ${runtimeSchema}`);
|
|
144
|
+
} else if (Array.isArray(options.schema)) {
|
|
145
|
+
runtimeSchema = options.schema.map((s) => resolveForSchema(s));
|
|
146
|
+
logger.info(`Resolved schema paths: ${runtimeSchema.join(", ")}`);
|
|
147
|
+
} else {
|
|
148
|
+
runtimeSchema = options.schema;
|
|
149
|
+
}
|
|
150
|
+
config.runtimeConfig = config.runtimeConfig || {};
|
|
151
|
+
config.runtimeConfig.grafserv = {
|
|
152
|
+
type: "schema",
|
|
153
|
+
schema: runtimeSchema,
|
|
154
|
+
resolversPath: resolverPath,
|
|
155
|
+
url: options.url || "/graphql/",
|
|
156
|
+
graphiql: options.graphiql
|
|
157
|
+
};
|
|
158
|
+
config.virtual = config.virtual || {};
|
|
159
|
+
if (resolverPath) {
|
|
160
|
+
config.virtual["#internal/grafserv/resolvers"] = `export { default } from '${resolverPath}'`;
|
|
161
|
+
}
|
|
77
162
|
}
|
|
78
163
|
config.externals = config.externals || {};
|
|
79
164
|
config.externals.external = config.externals.external || [];
|
|
80
165
|
config.externals.external.push(
|
|
166
|
+
"graphql",
|
|
81
167
|
"grafast",
|
|
168
|
+
"postgraphile",
|
|
169
|
+
"graphile-config",
|
|
170
|
+
"graphile-build",
|
|
82
171
|
"@graphql-tools/schema",
|
|
83
172
|
"@graphql-tools/load",
|
|
84
173
|
"@graphql-tools/graphql-file-loader"
|
|
@@ -86,6 +175,11 @@ const module$1 = defineNuxtModule({
|
|
|
86
175
|
const grafastPath = require.resolve("grafast");
|
|
87
176
|
config.alias = config.alias || {};
|
|
88
177
|
config.alias["grafast"] = grafastPath;
|
|
178
|
+
config.typescript = config.typescript || {};
|
|
179
|
+
config.typescript.tsConfig = config.typescript.tsConfig || {};
|
|
180
|
+
config.typescript.tsConfig.compilerOptions = config.typescript.tsConfig.compilerOptions || {};
|
|
181
|
+
config.typescript.tsConfig.compilerOptions.allowImportingTsExtensions = true;
|
|
182
|
+
config.typescript.tsConfig.compilerOptions.moduleResolution = "bundler";
|
|
89
183
|
});
|
|
90
184
|
nuxt.hook("nitro:config", (config) => {
|
|
91
185
|
config.handlers = config.handlers || [];
|
|
@@ -119,8 +213,9 @@ const module$1 = defineNuxtModule({
|
|
|
119
213
|
let cacheClearing = false;
|
|
120
214
|
nuxt.hook("builder:watch", async (event, path) => {
|
|
121
215
|
const isSchemaFile = path.endsWith(".graphql");
|
|
122
|
-
const isResolverFile = options.resolvers && path.includes(options.resolvers.replace("./", ""));
|
|
123
|
-
|
|
216
|
+
const isResolverFile = options.type === "schema" && options.resolvers && path.includes(options.resolvers.replace("./", ""));
|
|
217
|
+
const isPresetFile = options.type === "postgraphile" && path.includes(options.preset.replace("./", ""));
|
|
218
|
+
if (isSchemaFile || isResolverFile || isPresetFile) {
|
|
124
219
|
if (!cacheClearing) {
|
|
125
220
|
cacheClearing = true;
|
|
126
221
|
try {
|
package/dist/runtime/handler.js
CHANGED
|
@@ -13,68 +13,69 @@ 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 (
|
|
25
|
-
console.debug("[@stonecrop/nuxt-grafserv]
|
|
21
|
+
if (options.type === "postgraphile") {
|
|
22
|
+
console.debug("[@stonecrop/nuxt-grafserv] Getting schema from PostGraphile instance");
|
|
26
23
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
schema = await options.schema.getSchema();
|
|
32
|
-
}
|
|
33
|
-
console.debug("[@stonecrop/nuxt-grafserv] PostGraphile schema loaded successfully");
|
|
24
|
+
const { pgl } = await import("#internal/grafserv/pgl");
|
|
25
|
+
console.debug("[@stonecrop/nuxt-grafserv] Calling pgl.getSchema()");
|
|
26
|
+
schema = await pgl.getSchema();
|
|
27
|
+
console.debug("[@stonecrop/nuxt-grafserv] PostGraphile schema retrieved successfully");
|
|
34
28
|
} catch (error) {
|
|
35
|
-
|
|
29
|
+
if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'[@stonecrop/nuxt-grafserv] PostGraphile preset provided but "postgraphile" package not found. Install it with: npm install postgraphile'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
console.error("[@stonecrop/nuxt-grafserv] Error getting PostGraphile schema:", error);
|
|
36
35
|
throw error;
|
|
37
36
|
}
|
|
38
|
-
} else if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
} else if (options.type === "schema") {
|
|
38
|
+
if (typeof options.schema === "function") {
|
|
39
|
+
console.debug("[@stonecrop/nuxt-grafserv] Using schema provider function");
|
|
40
|
+
schema = await options.schema();
|
|
41
|
+
} else {
|
|
42
|
+
console.debug("[@stonecrop/nuxt-grafserv] Loading schema from file(s)");
|
|
43
|
+
const typeDefDocs = await loadTypeDefsFromFiles(options.schema);
|
|
44
|
+
const objects = {};
|
|
45
|
+
if (options.resolversPath) {
|
|
46
|
+
try {
|
|
47
|
+
const resolverModule = await import("#internal/grafserv/resolvers");
|
|
48
|
+
const resolvers = resolverModule.default || resolverModule;
|
|
49
|
+
console.debug("[@stonecrop/nuxt-grafserv] Resolvers loaded:", Object.keys(resolvers));
|
|
50
|
+
for (const typeName of Object.keys(resolvers)) {
|
|
51
|
+
const typeResolvers = resolvers[typeName];
|
|
52
|
+
if (typeResolvers && typeof typeResolvers === "object" && "plans" in typeResolvers) {
|
|
53
|
+
objects[typeName] = typeResolvers;
|
|
54
|
+
} else {
|
|
55
|
+
objects[typeName] = { plans: typeResolvers };
|
|
56
|
+
}
|
|
54
57
|
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error("[@stonecrop/nuxt-grafserv] Error loading resolvers:", e);
|
|
60
|
+
throw e;
|
|
55
61
|
}
|
|
56
|
-
}
|
|
57
|
-
console.
|
|
58
|
-
|
|
62
|
+
} else {
|
|
63
|
+
console.debug("[@stonecrop/nuxt-grafserv] No resolvers specified");
|
|
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;
|
|
59
74
|
}
|
|
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
75
|
}
|
|
75
76
|
} else {
|
|
76
77
|
throw new Error(
|
|
77
|
-
|
|
78
|
+
`[@stonecrop/nuxt-grafserv] Invalid configuration type: ${options.type}`
|
|
78
79
|
);
|
|
79
80
|
}
|
|
80
81
|
cachedSchema = schema;
|
|
@@ -86,7 +87,7 @@ export async function getGrafservInstance(options) {
|
|
|
86
87
|
return grafservInstance;
|
|
87
88
|
}
|
|
88
89
|
const schema = await getSchema(options);
|
|
89
|
-
grafservInstance = grafserv({ schema
|
|
90
|
+
grafservInstance = grafserv({ schema });
|
|
90
91
|
console.log("[@stonecrop/nuxt-grafserv] Grafserv instance created");
|
|
91
92
|
return grafservInstance;
|
|
92
93
|
}
|
package/dist/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/nuxt-grafserv",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
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.
|
|
46
|
+
"@stonecrop/graphql-middleware": "0.7.9"
|
|
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": "
|
|
76
|
-
"@stonecrop/rockfoil": "
|
|
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": {
|