nitro-graphql 2.0.0-beta.25 → 2.0.0-beta.26
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 +99 -532
- package/dist/rollup.mjs +2 -2
- package/dist/routes/apollo-server.d.mts +2 -2
- package/dist/routes/debug.d.mts +2 -2
- package/dist/routes/graphql-yoga.d.mts +2 -2
- package/dist/routes/health.d.mts +2 -2
- package/dist/setup.mjs +37 -46
- package/dist/utils/client-codegen.d.mts +4 -1
- package/dist/utils/client-codegen.mjs +2 -2
- package/dist/utils/index.mjs +14 -4
- package/dist/utils/type-generation.d.mts +7 -2
- package/dist/utils/type-generation.mjs +20 -19
- package/package.json +58 -66
package/README.md
CHANGED
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
|
|
57
57
|
**GraphQL Yoga (recommended):**
|
|
58
58
|
```bash
|
|
59
|
-
pnpm add nitro-graphql graphql-yoga graphql graphql-config
|
|
59
|
+
pnpm add nitro-graphql@beta graphql-yoga graphql graphql-config
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
**Apollo Server:**
|
|
63
63
|
```bash
|
|
64
|
-
pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired graphql graphql-config
|
|
64
|
+
pnpm add nitro-graphql@beta @apollo/server @apollo/utils.withrequired graphql graphql-config
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
### 2. Configure
|
|
@@ -71,13 +71,15 @@ pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired graphql graphql
|
|
|
71
71
|
|
|
72
72
|
```ts
|
|
73
73
|
// nitro.config.ts
|
|
74
|
+
import graphql from 'nitro-graphql'
|
|
74
75
|
import { defineNitroConfig } from 'nitro/config'
|
|
75
76
|
|
|
76
77
|
export default defineNitroConfig({
|
|
77
|
-
modules: [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
modules: [
|
|
79
|
+
graphql({
|
|
80
|
+
framework: 'graphql-yoga', // or 'apollo-server'
|
|
81
|
+
}),
|
|
82
|
+
],
|
|
81
83
|
})
|
|
82
84
|
```
|
|
83
85
|
|
|
@@ -90,18 +92,20 @@ export default defineNitroConfig({
|
|
|
90
92
|
// vite.config.ts
|
|
91
93
|
import { defineConfig } from 'vite'
|
|
92
94
|
import { nitro } from 'nitro/vite'
|
|
93
|
-
import
|
|
95
|
+
import graphql from 'nitro-graphql'
|
|
96
|
+
import { graphql as graphqlVite } from 'nitro-graphql/vite'
|
|
94
97
|
|
|
95
98
|
export default defineConfig({
|
|
96
99
|
plugins: [
|
|
97
|
-
|
|
100
|
+
graphqlVite(), // ⚠️ Must be before nitro()
|
|
98
101
|
nitro(),
|
|
99
102
|
],
|
|
100
103
|
nitro: {
|
|
101
|
-
modules: [
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
modules: [
|
|
105
|
+
graphql({
|
|
106
|
+
framework: 'graphql-yoga',
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
105
109
|
},
|
|
106
110
|
})
|
|
107
111
|
```
|
|
@@ -281,12 +285,17 @@ Disable all scaffold files for library/module development:
|
|
|
281
285
|
|
|
282
286
|
```ts
|
|
283
287
|
// nitro.config.ts
|
|
288
|
+
import graphql from 'nitro-graphql'
|
|
289
|
+
import { defineNitroConfig } from 'nitro/config'
|
|
290
|
+
|
|
284
291
|
export default defineNitroConfig({
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
292
|
+
modules: [
|
|
293
|
+
graphql({
|
|
294
|
+
framework: 'graphql-yoga',
|
|
295
|
+
scaffold: false, // Disable all scaffold files
|
|
296
|
+
clientUtils: false, // Disable client utilities
|
|
297
|
+
}),
|
|
298
|
+
],
|
|
290
299
|
})
|
|
291
300
|
```
|
|
292
301
|
|
|
@@ -295,37 +304,42 @@ export default defineNitroConfig({
|
|
|
295
304
|
Control each file individually:
|
|
296
305
|
|
|
297
306
|
```ts
|
|
307
|
+
import graphql from 'nitro-graphql'
|
|
308
|
+
import { defineNitroConfig } from 'nitro/config'
|
|
309
|
+
|
|
298
310
|
export default defineNitroConfig({
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// Scaffold files
|
|
303
|
-
scaffold: {
|
|
304
|
-
graphqlConfig: false, // Don't generate graphql.config.ts
|
|
305
|
-
serverSchema: true, // Generate server/graphql/schema.ts
|
|
306
|
-
serverConfig: true, // Generate server/graphql/config.ts
|
|
307
|
-
serverContext: false, // Don't generate server/graphql/context.ts
|
|
308
|
-
},
|
|
311
|
+
modules: [
|
|
312
|
+
graphql({
|
|
313
|
+
framework: 'graphql-yoga',
|
|
309
314
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
// Scaffold files
|
|
316
|
+
scaffold: {
|
|
317
|
+
graphqlConfig: false, // Don't generate graphql.config.ts
|
|
318
|
+
serverSchema: true, // Generate server/graphql/schema.ts
|
|
319
|
+
serverConfig: true, // Generate server/graphql/config.ts
|
|
320
|
+
serverContext: false, // Don't generate server/graphql/context.ts
|
|
321
|
+
},
|
|
315
322
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
323
|
+
// Client utilities (Nuxt only)
|
|
324
|
+
clientUtils: {
|
|
325
|
+
index: true, // Generate app/graphql/index.ts
|
|
326
|
+
ofetch: false, // Don't generate ofetch wrappers
|
|
327
|
+
},
|
|
321
328
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
+
// SDK files
|
|
330
|
+
sdk: {
|
|
331
|
+
main: true, // Generate default SDK
|
|
332
|
+
external: true, // Generate external service SDKs
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
// Type files
|
|
336
|
+
types: {
|
|
337
|
+
server: true, // Generate server types
|
|
338
|
+
client: true, // Generate client types
|
|
339
|
+
external: true, // Generate external service types
|
|
340
|
+
},
|
|
341
|
+
}),
|
|
342
|
+
],
|
|
329
343
|
})
|
|
330
344
|
```
|
|
331
345
|
|
|
@@ -334,34 +348,39 @@ export default defineNitroConfig({
|
|
|
334
348
|
Customize where files are generated:
|
|
335
349
|
|
|
336
350
|
```ts
|
|
351
|
+
import graphql from 'nitro-graphql'
|
|
352
|
+
import { defineNitroConfig } from 'nitro/config'
|
|
353
|
+
|
|
337
354
|
export default defineNitroConfig({
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// Method 1: Global paths (affects all files)
|
|
342
|
-
paths: {
|
|
343
|
-
serverGraphql: 'src/server/graphql',
|
|
344
|
-
clientGraphql: 'src/client/graphql',
|
|
345
|
-
buildDir: '.build',
|
|
346
|
-
typesDir: '.build/types',
|
|
347
|
-
},
|
|
355
|
+
modules: [
|
|
356
|
+
graphql({
|
|
357
|
+
framework: 'graphql-yoga',
|
|
348
358
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
359
|
+
// Method 1: Global paths (affects all files)
|
|
360
|
+
paths: {
|
|
361
|
+
serverGraphql: 'src/server/graphql',
|
|
362
|
+
clientGraphql: 'src/client/graphql',
|
|
363
|
+
buildDir: '.build',
|
|
364
|
+
typesDir: '.build/types',
|
|
365
|
+
},
|
|
354
366
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
// Method 2: Specific file paths
|
|
368
|
+
scaffold: {
|
|
369
|
+
serverSchema: 'lib/graphql/schema.ts',
|
|
370
|
+
serverConfig: 'lib/graphql/config.ts',
|
|
371
|
+
},
|
|
359
372
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
sdk: {
|
|
374
|
+
main: 'app/graphql/organization/sdk.ts',
|
|
375
|
+
external: 'app/graphql/{serviceName}/client-sdk.ts',
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
types: {
|
|
379
|
+
server: 'types/graphql-server.d.ts',
|
|
380
|
+
client: 'types/graphql-client.d.ts',
|
|
381
|
+
},
|
|
382
|
+
}),
|
|
383
|
+
],
|
|
365
384
|
})
|
|
366
385
|
```
|
|
367
386
|
|
|
@@ -576,14 +595,19 @@ Build federated GraphQL services:
|
|
|
576
595
|
|
|
577
596
|
```ts
|
|
578
597
|
// nitro.config.ts
|
|
598
|
+
import graphql from 'nitro-graphql'
|
|
599
|
+
import { defineNitroConfig } from 'nitro/config'
|
|
600
|
+
|
|
579
601
|
export default defineNitroConfig({
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
602
|
+
modules: [
|
|
603
|
+
graphql({
|
|
604
|
+
framework: 'apollo-server',
|
|
605
|
+
federation: {
|
|
606
|
+
enabled: true,
|
|
607
|
+
serviceName: 'users-service',
|
|
608
|
+
},
|
|
609
|
+
}),
|
|
610
|
+
],
|
|
587
611
|
})
|
|
588
612
|
```
|
|
589
613
|
|
|
@@ -773,463 +797,6 @@ This package powers production applications:
|
|
|
773
797
|
|
|
774
798
|
- [**Nitroping**](https://github.com/productdevbook/nitroping) - Self-hosted push notification service
|
|
775
799
|
|
|
776
|
-
## 🤖 Using Claude Code
|
|
777
|
-
|
|
778
|
-
Speed up development with [Claude Code](https://claude.ai/code) — AI-powered assistance for setting up and building with nitro-graphql.
|
|
779
|
-
|
|
780
|
-
### Quick Setup Prompts
|
|
781
|
-
|
|
782
|
-
Copy and paste these prompts into Claude Code to scaffold a complete GraphQL API.
|
|
783
|
-
|
|
784
|
-
**💡 Tip**: After pasting, Claude Code will execute step-by-step and validate each action.
|
|
785
|
-
|
|
786
|
-
<details>
|
|
787
|
-
<summary>🟢 <strong>Nuxt Project</strong></summary>
|
|
788
|
-
|
|
789
|
-
```
|
|
790
|
-
## GOAL
|
|
791
|
-
Set up nitro-graphql in this Nuxt project with a User management GraphQL API.
|
|
792
|
-
|
|
793
|
-
## PREREQUISITES
|
|
794
|
-
Check if this is a Nuxt project by looking for nuxt.config.ts in the root.
|
|
795
|
-
|
|
796
|
-
## STEP 1: INSTALL DEPENDENCIES
|
|
797
|
-
Action: Run this command
|
|
798
|
-
Command: pnpm add nitro-graphql graphql-yoga graphql
|
|
799
|
-
Validation: Check package.json contains these packages
|
|
800
|
-
|
|
801
|
-
## STEP 2: CONFIGURE NUXT
|
|
802
|
-
File: nuxt.config.ts
|
|
803
|
-
Action: EDIT (add to existing config, don't replace)
|
|
804
|
-
Add these properties:
|
|
805
|
-
|
|
806
|
-
export default defineNuxtConfig({
|
|
807
|
-
modules: ['nitro-graphql/nuxt'], // Add this module
|
|
808
|
-
nitro: {
|
|
809
|
-
graphql: {
|
|
810
|
-
framework: 'graphql-yoga',
|
|
811
|
-
},
|
|
812
|
-
},
|
|
813
|
-
})
|
|
814
|
-
|
|
815
|
-
Validation: Check the file has modules array and nitro.graphql config
|
|
816
|
-
|
|
817
|
-
## STEP 3: CREATE SCHEMA
|
|
818
|
-
File: server/graphql/schema.graphql
|
|
819
|
-
Action: CREATE NEW FILE (create server/graphql/ directory if needed)
|
|
820
|
-
Content:
|
|
821
|
-
|
|
822
|
-
type User {
|
|
823
|
-
id: ID!
|
|
824
|
-
name: String!
|
|
825
|
-
email: String!
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
type Query {
|
|
829
|
-
users: [User!]!
|
|
830
|
-
user(id: ID!): User
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
type Mutation {
|
|
834
|
-
_empty: String
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
Validation: File should be in server/graphql/ directory
|
|
838
|
-
|
|
839
|
-
## STEP 4: CREATE CONTEXT (Optional but recommended)
|
|
840
|
-
File: server/graphql/context.ts
|
|
841
|
-
Action: CREATE NEW FILE (auto-generated on first run, but create manually for clarity)
|
|
842
|
-
Content:
|
|
843
|
-
|
|
844
|
-
// Extend H3 event context with custom properties
|
|
845
|
-
declare module 'h3' {
|
|
846
|
-
interface H3EventContext {
|
|
847
|
-
// Add your custom context properties here
|
|
848
|
-
// Example:
|
|
849
|
-
// db?: Database
|
|
850
|
-
// auth?: { userId: string }
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
Note: This file lets you add custom properties to resolver context
|
|
855
|
-
Validation: File exists in server/graphql/
|
|
856
|
-
|
|
857
|
-
## STEP 5: CREATE CONFIG (Optional)
|
|
858
|
-
File: server/graphql/config.ts
|
|
859
|
-
Action: CREATE NEW FILE (auto-generated, customize if needed)
|
|
860
|
-
Content:
|
|
861
|
-
|
|
862
|
-
// Custom GraphQL Yoga configuration
|
|
863
|
-
export default defineGraphQLConfig({
|
|
864
|
-
// Custom context enhancer, plugins, etc.
|
|
865
|
-
// See: https://the-guild.dev/graphql/yoga-server/docs
|
|
866
|
-
})
|
|
867
|
-
|
|
868
|
-
Note: Use this to customize GraphQL Yoga options
|
|
869
|
-
Validation: File exists in server/graphql/
|
|
870
|
-
|
|
871
|
-
## STEP 6: CREATE RESOLVERS
|
|
872
|
-
File: server/graphql/users.resolver.ts
|
|
873
|
-
Action: CREATE NEW FILE
|
|
874
|
-
Content:
|
|
875
|
-
|
|
876
|
-
// Must explicitly import (NOT auto-imported)
|
|
877
|
-
import { defineQuery } from 'nitro-graphql/define'
|
|
878
|
-
|
|
879
|
-
// ⚠️ CRITICAL: Use NAMED EXPORTS (not default export)
|
|
880
|
-
export const userQueries = defineQuery({
|
|
881
|
-
users: async (_, __, context) => {
|
|
882
|
-
// context is H3EventContext - access event, storage, etc.
|
|
883
|
-
return [
|
|
884
|
-
{ id: '1', name: 'John Doe', email: 'john@example.com' },
|
|
885
|
-
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
|
|
886
|
-
]
|
|
887
|
-
},
|
|
888
|
-
user: async (_, { id }, context) => {
|
|
889
|
-
// Third parameter is context (H3EventContext)
|
|
890
|
-
const users = [
|
|
891
|
-
{ id: '1', name: 'John Doe', email: 'john@example.com' },
|
|
892
|
-
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
|
|
893
|
-
]
|
|
894
|
-
return users.find(u => u.id === id) || null
|
|
895
|
-
}
|
|
896
|
-
})
|
|
897
|
-
|
|
898
|
-
Validation: File ends with .resolver.ts, has import statement, and uses named export
|
|
899
|
-
|
|
900
|
-
## STEP 7: START DEV SERVER
|
|
901
|
-
Command: pnpm dev
|
|
902
|
-
Expected Output: Server starts on http://localhost:3000
|
|
903
|
-
Wait for: "Nitro built in X ms" message
|
|
904
|
-
Note: context.ts and config.ts will auto-generate if you skipped steps 4-5
|
|
905
|
-
|
|
906
|
-
## VALIDATION CHECKLIST
|
|
907
|
-
- [ ] Navigate to http://localhost:3000/api/graphql - should show GraphQL playground
|
|
908
|
-
- [ ] Health check: http://localhost:3000/api/graphql/health - should return OK
|
|
909
|
-
- [ ] Run this query in playground:
|
|
910
|
-
```graphql
|
|
911
|
-
query {
|
|
912
|
-
users {
|
|
913
|
-
id
|
|
914
|
-
name
|
|
915
|
-
email
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
```
|
|
919
|
-
Expected: Returns 2 users
|
|
920
|
-
- [ ] Check .nuxt/types/nitro-graphql-server.d.ts exists (types auto-generated)
|
|
921
|
-
|
|
922
|
-
## FILE STRUCTURE CREATED
|
|
923
|
-
```
|
|
924
|
-
server/
|
|
925
|
-
graphql/
|
|
926
|
-
schema.graphql ← GraphQL type definitions
|
|
927
|
-
context.ts ← H3 event context augmentation (optional)
|
|
928
|
-
config.ts ← GraphQL Yoga config (optional)
|
|
929
|
-
users.resolver.ts ← Query resolvers
|
|
930
|
-
.nuxt/
|
|
931
|
-
types/
|
|
932
|
-
nitro-graphql-server.d.ts ← Auto-generated types
|
|
933
|
-
graphql.config.ts ← Auto-generated (for IDE tooling)
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
## CRITICAL RULES (MUST FOLLOW)
|
|
937
|
-
❌ DO NOT use default exports in resolvers
|
|
938
|
-
Wrong: export default defineQuery({...})
|
|
939
|
-
Right: export const userQueries = defineQuery({...})
|
|
940
|
-
|
|
941
|
-
❌ DO NOT name files without .resolver.ts extension
|
|
942
|
-
Wrong: users.ts or user-resolver.ts
|
|
943
|
-
Right: users.resolver.ts or user.resolver.ts
|
|
944
|
-
|
|
945
|
-
✅ DO use named exports for all resolvers
|
|
946
|
-
✅ DO place files in server/graphql/ directory
|
|
947
|
-
✅ DO restart dev server if types don't generate
|
|
948
|
-
|
|
949
|
-
## TROUBLESHOOTING
|
|
950
|
-
Issue: "GraphQL endpoint returns 404"
|
|
951
|
-
Fix: Ensure 'nitro-graphql/nuxt' is in modules array (not just 'nitro-graphql')
|
|
952
|
-
|
|
953
|
-
Issue: "defineQuery is not defined"
|
|
954
|
-
Fix: Add import statement at top of resolver file:
|
|
955
|
-
import { defineQuery } from 'nitro-graphql/define'
|
|
956
|
-
|
|
957
|
-
Issue: "Types not generating"
|
|
958
|
-
Fix: Check .nuxt/types/nitro-graphql-server.d.ts exists, if not restart dev server
|
|
959
|
-
|
|
960
|
-
Issue: "Module not found: nitro-graphql"
|
|
961
|
-
Fix: Run pnpm install again, check package.json has the package
|
|
962
|
-
|
|
963
|
-
## NEXT STEPS (After Setup Works)
|
|
964
|
-
1. Add mutations: "Add createUser and deleteUser mutations with H3 storage"
|
|
965
|
-
2. Extend context: "Add database connection to context.ts and use it in resolvers"
|
|
966
|
-
3. Use types: "Import and use TypeScript types from #graphql/server in resolvers"
|
|
967
|
-
4. Add auth: "Add authentication middleware using context in resolvers"
|
|
968
|
-
5. Custom config: "Configure GraphQL Yoga plugins in config.ts"
|
|
969
|
-
|
|
970
|
-
Now implement this setup step-by-step.
|
|
971
|
-
```
|
|
972
|
-
|
|
973
|
-
</details>
|
|
974
|
-
|
|
975
|
-
<details>
|
|
976
|
-
<summary>⚡ <strong>Nitro Project</strong></summary>
|
|
977
|
-
|
|
978
|
-
```
|
|
979
|
-
Set up nitro-graphql in this Nitro project following these exact specifications:
|
|
980
|
-
|
|
981
|
-
INSTALLATION:
|
|
982
|
-
1. Run: pnpm add nitro-graphql graphql-yoga graphql
|
|
983
|
-
|
|
984
|
-
CONFIGURATION (nitro.config.ts):
|
|
985
|
-
import { defineNitroConfig } from 'nitro/config'
|
|
986
|
-
|
|
987
|
-
export default defineNitroConfig({
|
|
988
|
-
modules: ['nitro-graphql'],
|
|
989
|
-
graphql: {
|
|
990
|
-
framework: 'graphql-yoga',
|
|
991
|
-
},
|
|
992
|
-
})
|
|
993
|
-
|
|
994
|
-
SCHEMA (server/graphql/schema.graphql):
|
|
995
|
-
type Product {
|
|
996
|
-
id: ID!
|
|
997
|
-
name: String!
|
|
998
|
-
price: Float!
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
input CreateProductInput {
|
|
1002
|
-
name: String!
|
|
1003
|
-
price: Float!
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
type Query {
|
|
1007
|
-
products: [Product!]!
|
|
1008
|
-
product(id: ID!): Product
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
type Mutation {
|
|
1012
|
-
createProduct(input: CreateProductInput!): Product!
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
RESOLVERS (server/graphql/products.resolver.ts):
|
|
1016
|
-
// Must explicitly import (NOT auto-imported)
|
|
1017
|
-
import { defineQuery, defineMutation } from 'nitro-graphql/define'
|
|
1018
|
-
|
|
1019
|
-
// Use NAMED EXPORTS only
|
|
1020
|
-
export const productQueries = defineQuery({
|
|
1021
|
-
products: async (_, __, context) => {
|
|
1022
|
-
// Access H3 event context
|
|
1023
|
-
const products = await context.storage?.getItem('products') || []
|
|
1024
|
-
return products
|
|
1025
|
-
},
|
|
1026
|
-
product: async (_, { id }, context) => {
|
|
1027
|
-
const products = await context.storage?.getItem('products') || []
|
|
1028
|
-
return products.find(p => p.id === id)
|
|
1029
|
-
}
|
|
1030
|
-
})
|
|
1031
|
-
|
|
1032
|
-
export const productMutations = defineMutation({
|
|
1033
|
-
createProduct: async (_, { input }, context) => {
|
|
1034
|
-
const products = await context.storage?.getItem('products') || []
|
|
1035
|
-
const product = {
|
|
1036
|
-
id: Date.now().toString(),
|
|
1037
|
-
...input
|
|
1038
|
-
}
|
|
1039
|
-
products.push(product)
|
|
1040
|
-
await context.storage?.setItem('products', products)
|
|
1041
|
-
return product
|
|
1042
|
-
}
|
|
1043
|
-
})
|
|
1044
|
-
|
|
1045
|
-
KEY RULES:
|
|
1046
|
-
- Files: *.graphql for schemas, *.resolver.ts for resolvers
|
|
1047
|
-
- MUST use named exports (not default export)
|
|
1048
|
-
- MUST explicitly import: import { defineQuery } from 'nitro-graphql/define'
|
|
1049
|
-
- Context is the third parameter (access H3 event context)
|
|
1050
|
-
- Endpoint: http://localhost:3000/api/graphql
|
|
1051
|
-
|
|
1052
|
-
Now implement this setup.
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
</details>
|
|
1056
|
-
|
|
1057
|
-
<details>
|
|
1058
|
-
<summary>🎮 <strong>Apollo Server Setup</strong></summary>
|
|
1059
|
-
|
|
1060
|
-
```
|
|
1061
|
-
Set up nitro-graphql with Apollo Server following these exact specifications:
|
|
1062
|
-
|
|
1063
|
-
INSTALLATION:
|
|
1064
|
-
1. Run: pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired graphql
|
|
1065
|
-
|
|
1066
|
-
CONFIGURATION (nitro.config.ts):
|
|
1067
|
-
import { defineNitroConfig } from 'nitro/config'
|
|
1068
|
-
|
|
1069
|
-
export default defineNitroConfig({
|
|
1070
|
-
modules: ['nitro-graphql'],
|
|
1071
|
-
graphql: {
|
|
1072
|
-
framework: 'apollo-server',
|
|
1073
|
-
},
|
|
1074
|
-
})
|
|
1075
|
-
|
|
1076
|
-
SCHEMA (server/graphql/schema.graphql):
|
|
1077
|
-
type Book {
|
|
1078
|
-
id: ID!
|
|
1079
|
-
title: String!
|
|
1080
|
-
author: String!
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
type Query {
|
|
1084
|
-
books: [Book!]!
|
|
1085
|
-
book(id: ID!): Book
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
type Mutation {
|
|
1089
|
-
addBook(title: String!, author: String!): Book!
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
RESOLVERS (server/graphql/books.resolver.ts):
|
|
1093
|
-
// Must explicitly import (NOT auto-imported)
|
|
1094
|
-
import { defineResolver } from 'nitro-graphql/define'
|
|
1095
|
-
|
|
1096
|
-
// IMPORTANT: Use NAMED EXPORTS
|
|
1097
|
-
export const bookResolver = defineResolver({
|
|
1098
|
-
Query: {
|
|
1099
|
-
books: async () => {
|
|
1100
|
-
return [
|
|
1101
|
-
{ id: '1', title: '1984', author: 'George Orwell' }
|
|
1102
|
-
]
|
|
1103
|
-
},
|
|
1104
|
-
book: async (_, { id }) => {
|
|
1105
|
-
return { id, title: '1984', author: 'George Orwell' }
|
|
1106
|
-
}
|
|
1107
|
-
},
|
|
1108
|
-
Mutation: {
|
|
1109
|
-
addBook: async (_, { title, author }) => {
|
|
1110
|
-
return {
|
|
1111
|
-
id: Date.now().toString(),
|
|
1112
|
-
title,
|
|
1113
|
-
author
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
})
|
|
1118
|
-
|
|
1119
|
-
KEY RULES:
|
|
1120
|
-
- framework: 'apollo-server' in config
|
|
1121
|
-
- MUST explicitly import: import { defineResolver } from 'nitro-graphql/define'
|
|
1122
|
-
- defineResolver for complete resolver maps
|
|
1123
|
-
- Named exports required (export const name = ...)
|
|
1124
|
-
- Apollo Sandbox: http://localhost:3000/api/graphql
|
|
1125
|
-
- Supports Apollo Federation with federation: { enabled: true }
|
|
1126
|
-
|
|
1127
|
-
Now implement this setup.
|
|
1128
|
-
```
|
|
1129
|
-
|
|
1130
|
-
</details>
|
|
1131
|
-
|
|
1132
|
-
<details>
|
|
1133
|
-
<summary>🔄 <strong>Add Feature to Existing Setup</strong></summary>
|
|
1134
|
-
|
|
1135
|
-
```
|
|
1136
|
-
Add a complete blog posts feature to my nitro-graphql API following these specifications:
|
|
1137
|
-
|
|
1138
|
-
SCHEMA (server/graphql/posts/post.graphql):
|
|
1139
|
-
type Post {
|
|
1140
|
-
id: ID!
|
|
1141
|
-
title: String!
|
|
1142
|
-
content: String!
|
|
1143
|
-
authorId: ID!
|
|
1144
|
-
createdAt: String!
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
input CreatePostInput {
|
|
1148
|
-
title: String!
|
|
1149
|
-
content: String!
|
|
1150
|
-
authorId: ID!
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
input UpdatePostInput {
|
|
1154
|
-
title: String
|
|
1155
|
-
content: String
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
extend type Query {
|
|
1159
|
-
posts(limit: Int = 10, offset: Int = 0): [Post!]!
|
|
1160
|
-
post(id: ID!): Post
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
extend type Mutation {
|
|
1164
|
-
createPost(input: CreatePostInput!): Post!
|
|
1165
|
-
updatePost(id: ID!, input: UpdatePostInput!): Post
|
|
1166
|
-
deletePost(id: ID!): Boolean!
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
RESOLVERS (server/graphql/posts/post.resolver.ts):
|
|
1170
|
-
// Must explicitly import (NOT auto-imported)
|
|
1171
|
-
import { defineQuery, defineMutation } from 'nitro-graphql/define'
|
|
1172
|
-
|
|
1173
|
-
// Use NAMED EXPORTS
|
|
1174
|
-
export const postQueries = defineQuery({
|
|
1175
|
-
posts: async (_, { limit, offset }, context) => {
|
|
1176
|
-
const posts = await context.storage?.getItem('posts') || []
|
|
1177
|
-
return posts.slice(offset, offset + limit)
|
|
1178
|
-
},
|
|
1179
|
-
post: async (_, { id }, context) => {
|
|
1180
|
-
const posts = await context.storage?.getItem('posts') || []
|
|
1181
|
-
return posts.find(p => p.id === id) || null
|
|
1182
|
-
}
|
|
1183
|
-
})
|
|
1184
|
-
|
|
1185
|
-
export const postMutations = defineMutation({
|
|
1186
|
-
createPost: async (_, { input }, context) => {
|
|
1187
|
-
const posts = await context.storage?.getItem('posts') || []
|
|
1188
|
-
const post = {
|
|
1189
|
-
id: Date.now().toString(),
|
|
1190
|
-
...input,
|
|
1191
|
-
createdAt: new Date().toISOString()
|
|
1192
|
-
}
|
|
1193
|
-
posts.push(post)
|
|
1194
|
-
await context.storage?.setItem('posts', posts)
|
|
1195
|
-
return post
|
|
1196
|
-
},
|
|
1197
|
-
updatePost: async (_, { id, input }, context) => {
|
|
1198
|
-
const posts = await context.storage?.getItem('posts') || []
|
|
1199
|
-
const index = posts.findIndex(p => p.id === id)
|
|
1200
|
-
if (index === -1) return null
|
|
1201
|
-
posts[index] = { ...posts[index], ...input }
|
|
1202
|
-
await context.storage?.setItem('posts', posts)
|
|
1203
|
-
return posts[index]
|
|
1204
|
-
},
|
|
1205
|
-
deletePost: async (_, { id }, context) => {
|
|
1206
|
-
const posts = await context.storage?.getItem('posts') || []
|
|
1207
|
-
const filtered = posts.filter(p => p.id !== id)
|
|
1208
|
-
await context.storage?.setItem('posts', filtered)
|
|
1209
|
-
return filtered.length < posts.length
|
|
1210
|
-
}
|
|
1211
|
-
})
|
|
1212
|
-
|
|
1213
|
-
TYPE USAGE:
|
|
1214
|
-
After dev server restarts, types are auto-generated in:
|
|
1215
|
-
- .nitro/types/nitro-graphql-server.d.ts (server types)
|
|
1216
|
-
- .nuxt/types/nitro-graphql-server.d.ts (for Nuxt)
|
|
1217
|
-
|
|
1218
|
-
Import types:
|
|
1219
|
-
import type { Post, CreatePostInput } from '#graphql/server'
|
|
1220
|
-
|
|
1221
|
-
KEY RULES:
|
|
1222
|
-
- MUST explicitly import: import { defineQuery, defineMutation } from 'nitro-graphql/define'
|
|
1223
|
-
- Use "extend type" to add to existing Query/Mutation
|
|
1224
|
-
- Named exports required
|
|
1225
|
-
- Context has H3 event properties
|
|
1226
|
-
- Types auto-generate on file changes
|
|
1227
|
-
|
|
1228
|
-
Now implement this feature.
|
|
1229
|
-
```
|
|
1230
|
-
|
|
1231
|
-
</details>
|
|
1232
|
-
|
|
1233
800
|
### Working with Your GraphQL API
|
|
1234
801
|
|
|
1235
802
|
Once set up, you can ask Claude Code for help with:
|
package/dist/rollup.mjs
CHANGED
|
@@ -94,8 +94,8 @@ async function rollupConfig(app) {
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
app.hooks.hook("dev:reload", async () => {
|
|
97
|
-
await serverTypeGeneration(app);
|
|
98
|
-
await clientTypeGeneration(app);
|
|
97
|
+
await serverTypeGeneration(app, { silent: true });
|
|
98
|
+
await clientTypeGeneration(app, { silent: true });
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
101
|
function virtualSchemas(app) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h30 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/apollo-server.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h30.EventHandlerWithFetch<h30.EventHandlerRequest, Promise<any>>;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { _default as default };
|
package/dist/routes/debug.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h31 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/debug.d.ts
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ import * as h35 from "h3";
|
|
|
10
10
|
* - /_nitro/graphql/debug - HTML dashboard
|
|
11
11
|
* - /_nitro/graphql/debug?format=json - JSON API
|
|
12
12
|
*/
|
|
13
|
-
declare const _default:
|
|
13
|
+
declare const _default: h31.EventHandlerWithFetch<h31.EventHandlerRequest, Promise<string | {
|
|
14
14
|
timestamp: string;
|
|
15
15
|
environment: {
|
|
16
16
|
dev: any;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h35 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/graphql-yoga.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h35.EventHandlerWithFetch<h35.EventHandlerRequest, Promise<Response>>;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { _default as default };
|
package/dist/routes/health.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h33 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/health.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h33.EventHandlerWithFetch<h33.EventHandlerRequest, Promise<{
|
|
5
5
|
status: string;
|
|
6
6
|
message: string;
|
|
7
7
|
timestamp: string;
|
package/dist/setup.mjs
CHANGED
|
@@ -12,20 +12,21 @@ import consola from "consola";
|
|
|
12
12
|
import { dirname, join, relative, resolve } from "pathe";
|
|
13
13
|
|
|
14
14
|
//#region src/setup.ts
|
|
15
|
+
const logger = consola.withTag("nitro-graphql");
|
|
15
16
|
/**
|
|
16
17
|
* Shared setup logic for nitro-graphql module
|
|
17
18
|
* Used by both the direct Nitro module export and the Vite plugin's nitro: hook
|
|
18
19
|
*/
|
|
19
20
|
async function setupNitroGraphQL(nitro) {
|
|
20
|
-
if (!nitro.options.graphql?.framework)
|
|
21
|
+
if (!nitro.options.graphql?.framework) logger.warn("No GraphQL framework specified. Please set graphql.framework to \"graphql-yoga\" or \"apollo-server\".");
|
|
21
22
|
if (nitro.options.graphql?.externalServices?.length) {
|
|
22
23
|
const validationErrors = validateExternalServices(nitro.options.graphql.externalServices);
|
|
23
24
|
if (validationErrors.length > 0) {
|
|
24
|
-
|
|
25
|
-
for (const error of validationErrors)
|
|
25
|
+
logger.error("External services configuration errors:");
|
|
26
|
+
for (const error of validationErrors) logger.error(` - ${error}`);
|
|
26
27
|
throw new Error("Invalid external services configuration");
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
logger.info(`Configured ${nitro.options.graphql.externalServices.length} external GraphQL services`);
|
|
29
30
|
}
|
|
30
31
|
const { getDefaultPaths } = await import("./utils/path-resolver.mjs");
|
|
31
32
|
const defaultPaths = getDefaultPaths(nitro);
|
|
@@ -59,7 +60,7 @@ async function setupNitroGraphQL(nitro) {
|
|
|
59
60
|
},
|
|
60
61
|
playground: true
|
|
61
62
|
});
|
|
62
|
-
if (nitro.options.graphql?.federation?.enabled)
|
|
63
|
+
if (nitro.options.graphql?.federation?.enabled) logger.info(`Apollo Federation enabled for service: ${nitro.options.graphql.federation.serviceName || "unnamed"}`);
|
|
63
64
|
const graphqlBuildDir = resolve(nitro.options.buildDir, "graphql");
|
|
64
65
|
nitro.graphql.buildDir = graphqlBuildDir;
|
|
65
66
|
const watchDirs = [];
|
|
@@ -95,11 +96,20 @@ async function setupNitroGraphQL(nitro) {
|
|
|
95
96
|
ignoreInitial: true,
|
|
96
97
|
ignored: [...nitro.options.ignore, ...generateLayerIgnorePatterns()]
|
|
97
98
|
}).on("all", async (_, path) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const isGraphQLFile = path.endsWith(".graphql") || path.endsWith(".gql");
|
|
100
|
+
const isResolverFile = path.endsWith(".resolver.ts") || path.endsWith(".resolver.js");
|
|
101
|
+
const isDirectiveFile = path.endsWith(".directive.ts") || path.endsWith(".directive.js");
|
|
102
|
+
if (isGraphQLFile || isResolverFile || isDirectiveFile) if (path.includes(nitro.graphql.serverDir) || path.includes("server/graphql") || path.includes("server\\graphql") || isResolverFile || isDirectiveFile) {
|
|
103
|
+
await scanResolvers(nitro).then((r) => nitro.scanResolvers = r);
|
|
104
|
+
await scanDirectives(nitro).then((d) => nitro.scanDirectives = d);
|
|
105
|
+
logger.success("Types regenerated");
|
|
106
|
+
await serverTypeGeneration(nitro, { silent: true });
|
|
107
|
+
await clientTypeGeneration(nitro, { silent: true });
|
|
101
108
|
await nitro.hooks.callHook("dev:reload");
|
|
102
|
-
} else
|
|
109
|
+
} else {
|
|
110
|
+
logger.success("Types regenerated");
|
|
111
|
+
await clientTypeGeneration(nitro, { silent: true });
|
|
112
|
+
}
|
|
103
113
|
});
|
|
104
114
|
nitro.hooks.hook("close", () => {
|
|
105
115
|
watcher.close();
|
|
@@ -111,33 +121,17 @@ async function setupNitroGraphQL(nitro) {
|
|
|
111
121
|
const directives = await scanDirectives(nitro);
|
|
112
122
|
nitro.scanDirectives = directives;
|
|
113
123
|
await generateDirectiveSchemas(nitro, directives);
|
|
124
|
+
let hasShownInitialLogs = false;
|
|
114
125
|
nitro.hooks.hook("dev:start", async () => {
|
|
115
|
-
|
|
116
|
-
nitro.scanSchemas = schemas;
|
|
126
|
+
nitro.scanSchemas = await scanSchemas(nitro);
|
|
117
127
|
const resolvers = await scanResolvers(nitro);
|
|
118
128
|
nitro.scanResolvers = resolvers;
|
|
119
129
|
const directives$1 = await scanDirectives(nitro);
|
|
120
130
|
nitro.scanDirectives = directives$1;
|
|
121
131
|
await generateDirectiveSchemas(nitro, directives$1);
|
|
122
|
-
|
|
123
|
-
nitro.
|
|
124
|
-
|
|
125
|
-
consola.box({
|
|
126
|
-
title: "Nitro GraphQL",
|
|
127
|
-
message: [
|
|
128
|
-
`Framework: ${nitro.options.graphql?.framework || "Not configured"}`,
|
|
129
|
-
`Schemas: ${schemas.length}`,
|
|
130
|
-
`Resolvers: ${resolvers.length}`,
|
|
131
|
-
`Directives: ${directives$1.length}`,
|
|
132
|
-
`Documents: ${docs.length}`,
|
|
133
|
-
"",
|
|
134
|
-
"Debug Dashboard: /_nitro/graphql/debug"
|
|
135
|
-
].join("\n"),
|
|
136
|
-
style: {
|
|
137
|
-
borderColor: "cyan",
|
|
138
|
-
borderStyle: "rounded"
|
|
139
|
-
}
|
|
140
|
-
});
|
|
132
|
+
nitro.scanDocuments = await scanDocs(nitro);
|
|
133
|
+
if (nitro.options.dev && !hasShownInitialLogs) {
|
|
134
|
+
hasShownInitialLogs = true;
|
|
141
135
|
if (resolvers.length > 0) {
|
|
142
136
|
const totalExports = resolvers.reduce((sum, r) => sum + r.imports.length, 0);
|
|
143
137
|
const typeCount = {
|
|
@@ -156,16 +150,16 @@ async function setupNitroGraphQL(nitro) {
|
|
|
156
150
|
if (typeCount.type > 0) breakdown.push(`${typeCount.type} type`);
|
|
157
151
|
if (typeCount.subscription > 0) breakdown.push(`${typeCount.subscription} subscription`);
|
|
158
152
|
if (typeCount.directive > 0) breakdown.push(`${typeCount.directive} directive`);
|
|
159
|
-
if (breakdown.length > 0)
|
|
160
|
-
} else
|
|
153
|
+
if (breakdown.length > 0) logger.success(`${totalExports} resolver export(s): ${breakdown.join(", ")}`);
|
|
154
|
+
} else logger.warn("No resolvers found. Check /_nitro/graphql/debug for details.");
|
|
161
155
|
}
|
|
162
156
|
});
|
|
163
157
|
await rollupConfig(nitro);
|
|
164
158
|
await serverTypeGeneration(nitro);
|
|
165
|
-
await clientTypeGeneration(nitro);
|
|
159
|
+
await clientTypeGeneration(nitro, { isInitial: true });
|
|
166
160
|
nitro.hooks.hook("close", async () => {
|
|
167
|
-
await serverTypeGeneration(nitro);
|
|
168
|
-
await clientTypeGeneration(nitro);
|
|
161
|
+
await serverTypeGeneration(nitro, { silent: true });
|
|
162
|
+
await clientTypeGeneration(nitro, { silent: true });
|
|
169
163
|
});
|
|
170
164
|
const runtime = fileURLToPath(new URL("routes", import.meta.url));
|
|
171
165
|
const methods = [
|
|
@@ -188,14 +182,11 @@ async function setupNitroGraphQL(nitro) {
|
|
|
188
182
|
handler: join(runtime, "health"),
|
|
189
183
|
method: "GET"
|
|
190
184
|
});
|
|
191
|
-
if (nitro.options.dev) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
});
|
|
197
|
-
consola.info("[nitro-graphql] Debug dashboard available at: /_nitro/graphql/debug");
|
|
198
|
-
}
|
|
185
|
+
if (nitro.options.dev) nitro.options.handlers.push({
|
|
186
|
+
route: "/_nitro/graphql/debug",
|
|
187
|
+
handler: join(runtime, "debug"),
|
|
188
|
+
method: "GET"
|
|
189
|
+
});
|
|
199
190
|
if (nitro.options.imports) {
|
|
200
191
|
nitro.options.imports.presets ??= [];
|
|
201
192
|
nitro.options.imports.presets.push({
|
|
@@ -380,10 +371,10 @@ declare module 'nitro/h3' {
|
|
|
380
371
|
export {}
|
|
381
372
|
`, "server context.d.ts");
|
|
382
373
|
if (existsSync(join(nitro.graphql.serverDir, "context.ts"))) {
|
|
383
|
-
|
|
384
|
-
|
|
374
|
+
logger.warn("Found context.ts file. Please rename it to context.d.ts for type-only definitions.");
|
|
375
|
+
logger.info("The context file should now be context.d.ts instead of context.ts");
|
|
385
376
|
}
|
|
386
|
-
} else
|
|
377
|
+
} else logger.info("Scaffold file generation is disabled (library mode)");
|
|
387
378
|
}
|
|
388
379
|
|
|
389
380
|
//#endregion
|
|
@@ -23,7 +23,10 @@ declare function loadExternalSchema(service: ExternalGraphQLService, buildDir?:
|
|
|
23
23
|
*/
|
|
24
24
|
declare function downloadAndSaveSchema(service: ExternalGraphQLService, buildDir: string): Promise<string | undefined>;
|
|
25
25
|
declare function loadGraphQLDocuments(patterns: string | string[]): Promise<Source[]>;
|
|
26
|
-
declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string, serviceName?: string, virtualTypesPath?: string
|
|
26
|
+
declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string, serviceName?: string, virtualTypesPath?: string, options?: {
|
|
27
|
+
silent?: boolean;
|
|
28
|
+
isInitial?: boolean;
|
|
29
|
+
}): Promise<false | {
|
|
27
30
|
types: string;
|
|
28
31
|
sdk: string;
|
|
29
32
|
}>;
|
|
@@ -165,9 +165,9 @@ async function loadGraphQLDocuments(patterns) {
|
|
|
165
165
|
else throw e;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName, virtualTypesPath) {
|
|
168
|
+
async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName, virtualTypesPath, options = {}) {
|
|
169
169
|
if (docs.length === 0 && !serviceName) {
|
|
170
|
-
consola$1.info("No client GraphQL files found. Skipping client type generation.");
|
|
170
|
+
if (!options.silent && options.isInitial) consola$1.info("No client GraphQL files found. Skipping client type generation.");
|
|
171
171
|
return false;
|
|
172
172
|
}
|
|
173
173
|
const serviceLabel = serviceName ? `:${serviceName}` : "";
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { directiveParser, generateDirectiveSchema, generateDirectiveSchemas } from "./directive-parser.mjs";
|
|
2
2
|
import { createDefaultMaskError } from "./errors.mjs";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join, relative } from "pathe";
|
|
4
|
+
import { basename, join, relative } from "pathe";
|
|
5
5
|
import { hash } from "ohash";
|
|
6
6
|
import { parseAsync } from "oxc-parser";
|
|
7
7
|
import { glob } from "tinyglobby";
|
|
@@ -77,6 +77,17 @@ async function scanResolvers(nitro) {
|
|
|
77
77
|
for (const file of files) try {
|
|
78
78
|
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
79
79
|
const parsed = await parseAsync(file.fullPath, fileContent);
|
|
80
|
+
if (parsed.errors && parsed.errors.length > 0) {
|
|
81
|
+
if (nitro.options.dev) {
|
|
82
|
+
const fileName = basename(file.fullPath);
|
|
83
|
+
const firstError = parsed.errors[0];
|
|
84
|
+
const location = firstError.labels?.[0];
|
|
85
|
+
const lineInfo = location ? `:${location.start}` : "";
|
|
86
|
+
const message = firstError.message.split(",")[0];
|
|
87
|
+
console.error(`✖ ${fileName}${lineInfo} - ${message}`);
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
80
91
|
const exports = {
|
|
81
92
|
imports: [],
|
|
82
93
|
specifier: file.fullPath
|
|
@@ -126,7 +137,6 @@ async function scanResolvers(nitro) {
|
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
if (nitro.options.dev) {
|
|
129
|
-
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
130
140
|
if (hasDefaultExport && !hasNamedExport) nitro.logger.warn(`[nitro-graphql] ${relPath}: Using default export instead of named export. Resolvers must use named exports like "export const myResolver = defineQuery(...)". Default exports are not detected.`);
|
|
131
141
|
if (exports.imports.length === 0 && hasNamedExport) {
|
|
132
142
|
const validFunctions = VALID_DEFINE_FUNCTIONS.join(", ");
|
|
@@ -136,8 +146,8 @@ async function scanResolvers(nitro) {
|
|
|
136
146
|
}
|
|
137
147
|
if (exports.imports.length > 0) exportName.push(exports);
|
|
138
148
|
} catch (error) {
|
|
139
|
-
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
140
|
-
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath}:`, error);
|
|
149
|
+
const relPath$1 = relative(nitro.options.rootDir, file.fullPath);
|
|
150
|
+
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath$1}:`, error);
|
|
141
151
|
}
|
|
142
152
|
return exportName;
|
|
143
153
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Nitro } from "nitro/types";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/type-generation.d.ts
|
|
4
|
-
declare function serverTypeGeneration(app: Nitro
|
|
5
|
-
|
|
4
|
+
declare function serverTypeGeneration(app: Nitro, options?: {
|
|
5
|
+
silent?: boolean;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
declare function clientTypeGeneration(nitro: Nitro, options?: {
|
|
8
|
+
silent?: boolean;
|
|
9
|
+
isInitial?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
6
11
|
//#endregion
|
|
7
12
|
export { clientTypeGeneration, serverTypeGeneration };
|
|
@@ -12,6 +12,7 @@ import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
|
12
12
|
import { printSchemaWithDirectives } from "@graphql-tools/utils";
|
|
13
13
|
|
|
14
14
|
//#region src/utils/type-generation.ts
|
|
15
|
+
const logger = consola.withTag("nitro-graphql");
|
|
15
16
|
function generateGraphQLIndexFile(nitro, clientDir, externalServices = []) {
|
|
16
17
|
if (!shouldGenerateClientUtils(nitro)) return;
|
|
17
18
|
const placeholders = getDefaultPaths(nitro);
|
|
@@ -238,15 +239,15 @@ function validateNoDuplicateTypes(schemas, schemaStrings) {
|
|
|
238
239
|
}
|
|
239
240
|
return true;
|
|
240
241
|
}
|
|
241
|
-
async function serverTypeGeneration(app) {
|
|
242
|
+
async function serverTypeGeneration(app, options = {}) {
|
|
242
243
|
try {
|
|
243
244
|
if (!shouldGenerateTypes(app)) {
|
|
244
|
-
|
|
245
|
+
logger.debug("Server type generation is disabled");
|
|
245
246
|
return;
|
|
246
247
|
}
|
|
247
248
|
const schemas = app.scanSchemas || [];
|
|
248
249
|
if (!schemas.length) {
|
|
249
|
-
consola.info("No GraphQL definitions found for server type generation.");
|
|
250
|
+
if (!options.silent) consola.info("No GraphQL definitions found for server type generation.");
|
|
250
251
|
return;
|
|
251
252
|
}
|
|
252
253
|
const schemaStrings = loadFilesSync(schemas).map((schema$1) => typeof schema$1 === "string" ? schema$1 : schema$1.loc?.source?.body || "").filter(Boolean);
|
|
@@ -269,18 +270,18 @@ async function serverTypeGeneration(app) {
|
|
|
269
270
|
if (serverTypesPath) {
|
|
270
271
|
mkdirSync(dirname(serverTypesPath), { recursive: true });
|
|
271
272
|
writeFileSync(serverTypesPath, data, "utf-8");
|
|
272
|
-
|
|
273
|
+
if (!options.silent) logger.success(`Generated server types at: ${serverTypesPath}`);
|
|
273
274
|
}
|
|
274
275
|
} catch (error) {
|
|
275
|
-
|
|
276
|
+
logger.error("Server schema generation error:", error);
|
|
276
277
|
}
|
|
277
278
|
}
|
|
278
|
-
async function clientTypeGeneration(nitro) {
|
|
279
|
+
async function clientTypeGeneration(nitro, options = {}) {
|
|
279
280
|
try {
|
|
280
|
-
if (nitro.scanSchemas && nitro.scanSchemas.length > 0) await generateMainClientTypes(nitro);
|
|
281
|
-
if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro);
|
|
281
|
+
if (nitro.scanSchemas && nitro.scanSchemas.length > 0) await generateMainClientTypes(nitro, options);
|
|
282
|
+
if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro, options);
|
|
282
283
|
} catch (error) {
|
|
283
|
-
|
|
284
|
+
logger.error("Client schema generation error:", error);
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
/**
|
|
@@ -311,17 +312,17 @@ function checkOldStructure(clientDir) {
|
|
|
311
312
|
🚫 The old files will cause import conflicts until moved!`);
|
|
312
313
|
}
|
|
313
314
|
}
|
|
314
|
-
async function generateMainClientTypes(nitro) {
|
|
315
|
+
async function generateMainClientTypes(nitro, options = {}) {
|
|
315
316
|
checkOldStructure(nitro.graphql.clientDir);
|
|
316
317
|
const docs = nitro.scanDocuments;
|
|
317
318
|
const loadDocs = await loadGraphQLDocuments(docs);
|
|
318
319
|
const schemaFilePath = join(nitro.graphql.buildDir, "schema.graphql");
|
|
319
320
|
if (!existsSync(schemaFilePath)) {
|
|
320
|
-
consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
|
|
321
|
+
if (!options.silent) consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
|
|
321
322
|
return;
|
|
322
323
|
}
|
|
323
324
|
const graphqlString = readFileSync(schemaFilePath, "utf-8");
|
|
324
|
-
const types = await generateClientTypes(nitro.options.graphql?.federation?.enabled === true ? buildSubgraphSchema([{ typeDefs: parse(graphqlString) }]) : buildSchema(graphqlString), loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {});
|
|
325
|
+
const types = await generateClientTypes(nitro.options.graphql?.federation?.enabled === true ? buildSubgraphSchema([{ typeDefs: parse(graphqlString) }]) : buildSchema(graphqlString), loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {}, void 0, void 0, void 0, options);
|
|
325
326
|
if (types === false) return;
|
|
326
327
|
const placeholders = getDefaultPaths(nitro);
|
|
327
328
|
const typesConfig = getTypesConfig(nitro);
|
|
@@ -330,22 +331,22 @@ async function generateMainClientTypes(nitro) {
|
|
|
330
331
|
if (clientTypesPath) {
|
|
331
332
|
mkdirSync(dirname(clientTypesPath), { recursive: true });
|
|
332
333
|
writeFileSync(clientTypesPath, types.types, "utf-8");
|
|
333
|
-
|
|
334
|
+
if (!options.silent) logger.success(`Generated client types at: ${clientTypesPath}`);
|
|
334
335
|
}
|
|
335
336
|
const sdkPath = resolveFilePath(sdkConfig.main, sdkConfig.enabled, true, "{clientGraphql}/default/sdk.ts", placeholders);
|
|
336
337
|
if (sdkPath) {
|
|
337
338
|
mkdirSync(dirname(sdkPath), { recursive: true });
|
|
338
339
|
writeFileSync(sdkPath, types.sdk, "utf-8");
|
|
339
|
-
|
|
340
|
+
if (!options.silent) logger.success(`Generated SDK at: ${sdkPath}`);
|
|
340
341
|
}
|
|
341
342
|
generateNuxtOfetchClient(nitro, nitro.graphql.clientDir, "default");
|
|
342
343
|
const externalServices = nitro.options.graphql?.externalServices || [];
|
|
343
344
|
if (externalServices.length > 0) generateGraphQLIndexFile(nitro, nitro.graphql.clientDir, externalServices);
|
|
344
345
|
}
|
|
345
|
-
async function generateExternalServicesTypes(nitro) {
|
|
346
|
+
async function generateExternalServicesTypes(nitro, options = {}) {
|
|
346
347
|
const externalServices = nitro.options.graphql?.externalServices || [];
|
|
347
348
|
for (const service of externalServices) try {
|
|
348
|
-
consola.info(`[graphql:${service.name}] Processing external service`);
|
|
349
|
+
if (!options.silent) consola.info(`[graphql:${service.name}] Processing external service`);
|
|
349
350
|
await downloadAndSaveSchema(service, nitro.options.buildDir);
|
|
350
351
|
const schema = await loadExternalSchema(service, nitro.options.buildDir);
|
|
351
352
|
if (!schema) {
|
|
@@ -379,16 +380,16 @@ async function generateExternalServicesTypes(nitro) {
|
|
|
379
380
|
if (serviceTypesPath) {
|
|
380
381
|
mkdirSync(dirname(serviceTypesPath), { recursive: true });
|
|
381
382
|
writeFileSync(serviceTypesPath, types.types, "utf-8");
|
|
382
|
-
consola.success(`[graphql:${service.name}] Generated types at: ${serviceTypesPath}`);
|
|
383
|
+
if (!options.silent) consola.success(`[graphql:${service.name}] Generated types at: ${serviceTypesPath}`);
|
|
383
384
|
}
|
|
384
385
|
const serviceSdkPath = resolveFilePath(service.paths?.sdk ?? sdkConfig.external, sdkConfig.enabled, true, "{clientGraphql}/{serviceName}/sdk.ts", placeholders);
|
|
385
386
|
if (serviceSdkPath) {
|
|
386
387
|
mkdirSync(dirname(serviceSdkPath), { recursive: true });
|
|
387
388
|
writeFileSync(serviceSdkPath, types.sdk, "utf-8");
|
|
388
|
-
consola.success(`[graphql:${service.name}] Generated SDK at: ${serviceSdkPath}`);
|
|
389
|
+
if (!options.silent) consola.success(`[graphql:${service.name}] Generated SDK at: ${serviceSdkPath}`);
|
|
389
390
|
}
|
|
390
391
|
generateExternalOfetchClient(nitro, service, service.endpoint);
|
|
391
|
-
consola.success(`[graphql:${service.name}] External service types generated successfully`);
|
|
392
|
+
if (!options.silent) consola.success(`[graphql:${service.name}] External service types generated successfully`);
|
|
392
393
|
} catch (error) {
|
|
393
394
|
consola.error(`[graphql:${service.name}] External service generation failed:`, error);
|
|
394
395
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-graphql",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
5
|
-
"packageManager": "pnpm@10.22.0",
|
|
4
|
+
"version": "2.0.0-beta.26",
|
|
6
5
|
"description": "GraphQL integration for Nitro",
|
|
7
6
|
"license": "MIT",
|
|
8
7
|
"sideEffects": false,
|
|
@@ -71,22 +70,6 @@
|
|
|
71
70
|
"files": [
|
|
72
71
|
"dist"
|
|
73
72
|
],
|
|
74
|
-
"scripts": {
|
|
75
|
-
"prepack": "pnpm build",
|
|
76
|
-
"build": "tsdown",
|
|
77
|
-
"dev": "tsdown --watch",
|
|
78
|
-
"bumpp": "bumpp package.json",
|
|
79
|
-
"release": "pnpm build && pnpm bumpp && pnpm publish --no-git-checks --access public",
|
|
80
|
-
"playground:nitro": "cd playgrounds/nitro && pnpm install && pnpm dev",
|
|
81
|
-
"playground:nuxt": "cd playgrounds/nuxt && pnpm install && pnpm dev",
|
|
82
|
-
"playground:federation": "cd playgrounds/federation && pnpm install && pnpm dev",
|
|
83
|
-
"docs:dev": "cd .docs && pnpm install && pnpm update:metadata && pnpm dev",
|
|
84
|
-
"docs:build": "cd .docs && pnpm install && pnpm update:metadata && pnpm build",
|
|
85
|
-
"docs:preview": "cd .docs && pnpm preview",
|
|
86
|
-
"lint": "eslint .",
|
|
87
|
-
"lint:fix": "eslint . --fix",
|
|
88
|
-
"test:types": "tsc --noEmit"
|
|
89
|
-
},
|
|
90
73
|
"peerDependencies": {
|
|
91
74
|
"@apollo/server": "^5.0.0",
|
|
92
75
|
"@apollo/utils.withrequired": "^3.0.0",
|
|
@@ -103,57 +86,66 @@
|
|
|
103
86
|
}
|
|
104
87
|
},
|
|
105
88
|
"dependencies": {
|
|
106
|
-
"@apollo/subgraph": "
|
|
107
|
-
"@graphql-codegen/core": "
|
|
108
|
-
"@graphql-codegen/import-types-preset": "
|
|
109
|
-
"@graphql-codegen/typescript": "
|
|
110
|
-
"@graphql-codegen/typescript-generic-sdk": "
|
|
111
|
-
"@graphql-codegen/typescript-operations": "
|
|
112
|
-
"@graphql-codegen/typescript-resolvers": "
|
|
113
|
-
"@graphql-tools/graphql-file-loader": "
|
|
114
|
-
"@graphql-tools/load": "
|
|
115
|
-
"@graphql-tools/load-files": "
|
|
116
|
-
"@graphql-tools/merge": "
|
|
117
|
-
"@graphql-tools/schema": "
|
|
118
|
-
"@graphql-tools/url-loader": "
|
|
119
|
-
"@graphql-tools/utils": "
|
|
120
|
-
"chokidar": "
|
|
121
|
-
"consola": "
|
|
122
|
-
"defu": "
|
|
123
|
-
"graphql-config": "
|
|
124
|
-
"graphql-scalars": "
|
|
125
|
-
"knitwork": "
|
|
126
|
-
"ohash": "
|
|
127
|
-
"oxc-parser": "
|
|
128
|
-
"pathe": "
|
|
129
|
-
"tinyglobby": "
|
|
89
|
+
"@apollo/subgraph": "^2.12.1",
|
|
90
|
+
"@graphql-codegen/core": "^5.0.0",
|
|
91
|
+
"@graphql-codegen/import-types-preset": "^3.0.1",
|
|
92
|
+
"@graphql-codegen/typescript": "^5.0.4",
|
|
93
|
+
"@graphql-codegen/typescript-generic-sdk": "^4.0.2",
|
|
94
|
+
"@graphql-codegen/typescript-operations": "^5.0.4",
|
|
95
|
+
"@graphql-codegen/typescript-resolvers": "^5.1.2",
|
|
96
|
+
"@graphql-tools/graphql-file-loader": "^8.1.6",
|
|
97
|
+
"@graphql-tools/load": "^8.1.6",
|
|
98
|
+
"@graphql-tools/load-files": "^7.0.1",
|
|
99
|
+
"@graphql-tools/merge": "^9.1.5",
|
|
100
|
+
"@graphql-tools/schema": "^10.0.29",
|
|
101
|
+
"@graphql-tools/url-loader": "^9.0.4",
|
|
102
|
+
"@graphql-tools/utils": "^10.10.3",
|
|
103
|
+
"chokidar": "^4.0.3",
|
|
104
|
+
"consola": "^3.4.2",
|
|
105
|
+
"defu": "^6.1.4",
|
|
106
|
+
"graphql-config": "^5.1.5",
|
|
107
|
+
"graphql-scalars": "^1.25.0",
|
|
108
|
+
"knitwork": "^1.3.0",
|
|
109
|
+
"ohash": "^2.0.11",
|
|
110
|
+
"oxc-parser": "^0.97.0",
|
|
111
|
+
"pathe": "^2.0.3",
|
|
112
|
+
"tinyglobby": "^0.2.15"
|
|
130
113
|
},
|
|
131
114
|
"devDependencies": {
|
|
132
|
-
"@antfu/eslint-config": "
|
|
133
|
-
"@nuxt/kit": "
|
|
134
|
-
"@nuxt/schema": "
|
|
135
|
-
"@types/node": "
|
|
136
|
-
"@vitejs/devtools": "
|
|
137
|
-
"bumpp": "
|
|
138
|
-
"changelogen": "
|
|
139
|
-
"crossws": "
|
|
140
|
-
"eslint": "
|
|
141
|
-
"graphql": "
|
|
142
|
-
"graphql-yoga": "
|
|
143
|
-
"h3": "
|
|
144
|
-
"nitro": "
|
|
145
|
-
"tsdown": "
|
|
146
|
-
"typescript": "
|
|
147
|
-
"vite": "
|
|
148
|
-
"vitepress-plugin-llms": "
|
|
149
|
-
},
|
|
150
|
-
"pnpm": {
|
|
151
|
-
"overrides": {
|
|
152
|
-
"nitro": "npm:nitro-nightly@latest",
|
|
153
|
-
"vite": "npm:rolldown-vite@latest"
|
|
154
|
-
}
|
|
115
|
+
"@antfu/eslint-config": "^6.2.0",
|
|
116
|
+
"@nuxt/kit": "^4.2.1",
|
|
117
|
+
"@nuxt/schema": "^4.2.1",
|
|
118
|
+
"@types/node": "^24.10.1",
|
|
119
|
+
"@vitejs/devtools": "^0.0.0-alpha.16",
|
|
120
|
+
"bumpp": "^10.3.1",
|
|
121
|
+
"changelogen": "^0.6.2",
|
|
122
|
+
"crossws": "^0.4.1",
|
|
123
|
+
"eslint": "^9.39.1",
|
|
124
|
+
"graphql": "^16.12.0",
|
|
125
|
+
"graphql-yoga": "^5.16.2",
|
|
126
|
+
"h3": "^2.0.1-rc.5",
|
|
127
|
+
"nitro": "npm:nitro-nightly@latest",
|
|
128
|
+
"tsdown": "^0.16.4",
|
|
129
|
+
"typescript": "^5.9.3",
|
|
130
|
+
"vite": "npm:rolldown-vite@latest",
|
|
131
|
+
"vitepress-plugin-llms": "^1.9.1"
|
|
155
132
|
},
|
|
156
133
|
"resolutions": {
|
|
157
134
|
"nitro-graphql": "link:."
|
|
135
|
+
},
|
|
136
|
+
"scripts": {
|
|
137
|
+
"build": "tsdown",
|
|
138
|
+
"dev": "tsdown --watch",
|
|
139
|
+
"bumpp": "bumpp package.json",
|
|
140
|
+
"release": "pnpm build && pnpm bumpp && pnpm publish --no-git-checks --access public",
|
|
141
|
+
"playground:nitro": "cd playgrounds/nitro && pnpm install && pnpm dev",
|
|
142
|
+
"playground:nuxt": "cd playgrounds/nuxt && pnpm install && pnpm dev",
|
|
143
|
+
"playground:federation": "cd playgrounds/federation && pnpm install && pnpm dev",
|
|
144
|
+
"docs:dev": "cd .docs && pnpm install && pnpm update:metadata && pnpm dev",
|
|
145
|
+
"docs:build": "cd .docs && pnpm install && pnpm update:metadata && pnpm build",
|
|
146
|
+
"docs:preview": "cd .docs && pnpm preview",
|
|
147
|
+
"lint": "eslint .",
|
|
148
|
+
"lint:fix": "eslint . --fix",
|
|
149
|
+
"test:types": "tsc --noEmit"
|
|
158
150
|
}
|
|
159
|
-
}
|
|
151
|
+
}
|