@biggora/claude-plugins 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/registry/registry.json +15 -0
- package/specs/coding.md +6 -0
- package/src/commands/skills/add.js +63 -7
- package/src/commands/skills/list.js +23 -52
- package/src/commands/skills/remove.js +26 -27
- package/src/commands/skills/resolve.js +155 -0
- package/src/commands/skills/update.js +58 -74
- package/src/skills/nest-best-practices/SKILL.md +251 -0
- package/src/skills/nest-best-practices/references/best-practices-request-lifecycle.md +158 -0
- package/src/skills/nest-best-practices/references/cli-monorepo.md +106 -0
- package/src/skills/nest-best-practices/references/cli-overview.md +157 -0
- package/src/skills/nest-best-practices/references/core-controllers.md +165 -0
- package/src/skills/nest-best-practices/references/core-dependency-injection.md +179 -0
- package/src/skills/nest-best-practices/references/core-middleware.md +139 -0
- package/src/skills/nest-best-practices/references/core-modules.md +138 -0
- package/src/skills/nest-best-practices/references/core-providers.md +188 -0
- package/src/skills/nest-best-practices/references/faq-raw-body-hybrid.md +122 -0
- package/src/skills/nest-best-practices/references/fundamentals-circular-dependency.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-custom-decorators.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-dynamic-modules.md +125 -0
- package/src/skills/nest-best-practices/references/fundamentals-exception-filters.md +202 -0
- package/src/skills/nest-best-practices/references/fundamentals-execution-context.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-guards.md +136 -0
- package/src/skills/nest-best-practices/references/fundamentals-interceptors.md +187 -0
- package/src/skills/nest-best-practices/references/fundamentals-lazy-loading.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-lifecycle-events.md +87 -0
- package/src/skills/nest-best-practices/references/fundamentals-module-reference.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-pipes.md +197 -0
- package/src/skills/nest-best-practices/references/fundamentals-provider-scopes.md +92 -0
- package/src/skills/nest-best-practices/references/fundamentals-testing.md +142 -0
- package/src/skills/nest-best-practices/references/graphql-overview.md +233 -0
- package/src/skills/nest-best-practices/references/graphql-resolvers-mutations.md +199 -0
- package/src/skills/nest-best-practices/references/graphql-scalars-unions-enums.md +180 -0
- package/src/skills/nest-best-practices/references/graphql-subscriptions.md +228 -0
- package/src/skills/nest-best-practices/references/microservices-grpc.md +175 -0
- package/src/skills/nest-best-practices/references/microservices-overview.md +221 -0
- package/src/skills/nest-best-practices/references/microservices-transports.md +119 -0
- package/src/skills/nest-best-practices/references/openapi-swagger.md +207 -0
- package/src/skills/nest-best-practices/references/recipes-authentication.md +97 -0
- package/src/skills/nest-best-practices/references/recipes-cqrs.md +176 -0
- package/src/skills/nest-best-practices/references/recipes-crud-generator.md +87 -0
- package/src/skills/nest-best-practices/references/recipes-documentation.md +93 -0
- package/src/skills/nest-best-practices/references/recipes-mongoose.md +153 -0
- package/src/skills/nest-best-practices/references/recipes-prisma.md +98 -0
- package/src/skills/nest-best-practices/references/recipes-terminus.md +148 -0
- package/src/skills/nest-best-practices/references/recipes-typeorm.md +122 -0
- package/src/skills/nest-best-practices/references/security-authorization.md +196 -0
- package/src/skills/nest-best-practices/references/security-cors-helmet-rate-limiting.md +204 -0
- package/src/skills/nest-best-practices/references/security-encryption-hashing.md +93 -0
- package/src/skills/nest-best-practices/references/techniques-caching.md +142 -0
- package/src/skills/nest-best-practices/references/techniques-compression-streaming-sse.md +194 -0
- package/src/skills/nest-best-practices/references/techniques-configuration.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-database.md +153 -0
- package/src/skills/nest-best-practices/references/techniques-events.md +163 -0
- package/src/skills/nest-best-practices/references/techniques-fastify.md +137 -0
- package/src/skills/nest-best-practices/references/techniques-file-upload.md +140 -0
- package/src/skills/nest-best-practices/references/techniques-http-module.md +176 -0
- package/src/skills/nest-best-practices/references/techniques-logging.md +146 -0
- package/src/skills/nest-best-practices/references/techniques-mvc-serve-static.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-queues.md +162 -0
- package/src/skills/nest-best-practices/references/techniques-serialization.md +158 -0
- package/src/skills/nest-best-practices/references/techniques-sessions-cookies.md +167 -0
- package/src/skills/nest-best-practices/references/techniques-task-scheduling.md +166 -0
- package/src/skills/nest-best-practices/references/techniques-validation.md +126 -0
- package/src/skills/nest-best-practices/references/techniques-versioning.md +153 -0
- package/src/skills/nest-best-practices/references/websockets-advanced.md +96 -0
- package/src/skills/nest-best-practices/references/websockets-gateways.md +215 -0
- package/src/skills/typescript-expert/SKILL.md +145 -0
- package/src/skills/typescript-expert/commands/typescript-fix.md +65 -0
- package/src/skills/typescript-expert/references/advanced-conditional-types.md +190 -0
- package/src/skills/typescript-expert/references/advanced-decorators.md +243 -0
- package/src/skills/typescript-expert/references/advanced-mapped-types.md +223 -0
- package/src/skills/typescript-expert/references/advanced-template-literals.md +209 -0
- package/src/skills/typescript-expert/references/advanced-type-guards.md +308 -0
- package/src/skills/typescript-expert/references/best-practices-patterns.md +313 -0
- package/src/skills/typescript-expert/references/best-practices-performance.md +185 -0
- package/src/skills/typescript-expert/references/best-practices-tsconfig.md +242 -0
- package/src/skills/typescript-expert/references/core-generics.md +246 -0
- package/src/skills/typescript-expert/references/core-interfaces-types.md +231 -0
- package/src/skills/typescript-expert/references/core-type-system.md +261 -0
- package/src/skills/typescript-expert/references/core-utility-types.md +235 -0
- package/src/skills/typescript-expert/references/features-ts5x.md +370 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql
|
|
3
|
+
description: Building GraphQL APIs with code-first and schema-first approaches
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GraphQL
|
|
7
|
+
|
|
8
|
+
NestJS provides GraphQL integration through Apollo Server or Mercurius.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Apollo (default)
|
|
14
|
+
npm install @nestjs/graphql @nestjs/apollo @apollo/server @as-integrations/express5 graphql
|
|
15
|
+
|
|
16
|
+
# Mercurius (Fastify)
|
|
17
|
+
npm install @nestjs/graphql @nestjs/mercurius graphql mercurius
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Module } from '@nestjs/common';
|
|
24
|
+
import { GraphQLModule } from '@nestjs/graphql';
|
|
25
|
+
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
|
|
28
|
+
@Module({
|
|
29
|
+
imports: [
|
|
30
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
31
|
+
driver: ApolloDriver,
|
|
32
|
+
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
|
33
|
+
// Or in-memory: autoSchemaFile: true
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
export class AppModule {}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Code-First Approach
|
|
41
|
+
|
|
42
|
+
### Object Type
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
|
|
46
|
+
|
|
47
|
+
@ObjectType()
|
|
48
|
+
export class Author {
|
|
49
|
+
@Field(() => ID)
|
|
50
|
+
id: string;
|
|
51
|
+
|
|
52
|
+
@Field()
|
|
53
|
+
name: string;
|
|
54
|
+
|
|
55
|
+
@Field(() => Int, { nullable: true })
|
|
56
|
+
age?: number;
|
|
57
|
+
|
|
58
|
+
@Field(() => [Post])
|
|
59
|
+
posts: Post[];
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Resolver
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
|
|
67
|
+
|
|
68
|
+
@Resolver(() => Author)
|
|
69
|
+
export class AuthorsResolver {
|
|
70
|
+
constructor(private authorsService: AuthorsService) {}
|
|
71
|
+
|
|
72
|
+
@Query(() => [Author], { name: 'authors' })
|
|
73
|
+
findAll() {
|
|
74
|
+
return this.authorsService.findAll();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Query(() => Author, { name: 'author' })
|
|
78
|
+
findOne(@Args('id', { type: () => ID }) id: string) {
|
|
79
|
+
return this.authorsService.findOne(id);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Mutation(() => Author)
|
|
83
|
+
createAuthor(@Args('input') input: CreateAuthorInput) {
|
|
84
|
+
return this.authorsService.create(input);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Input Type
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { InputType, Field } from '@nestjs/graphql';
|
|
93
|
+
|
|
94
|
+
@InputType()
|
|
95
|
+
export class CreateAuthorInput {
|
|
96
|
+
@Field()
|
|
97
|
+
name: string;
|
|
98
|
+
|
|
99
|
+
@Field({ nullable: true })
|
|
100
|
+
bio?: string;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Schema-First Approach
|
|
105
|
+
|
|
106
|
+
### GraphQL Schema
|
|
107
|
+
|
|
108
|
+
```graphql
|
|
109
|
+
# authors.graphql
|
|
110
|
+
type Author {
|
|
111
|
+
id: ID!
|
|
112
|
+
name: String!
|
|
113
|
+
posts: [Post!]!
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type Query {
|
|
117
|
+
authors: [Author!]!
|
|
118
|
+
author(id: ID!): Author
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Configuration
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
126
|
+
driver: ApolloDriver,
|
|
127
|
+
typePaths: ['./**/*.graphql'],
|
|
128
|
+
definitions: {
|
|
129
|
+
path: join(process.cwd(), 'src/graphql.ts'),
|
|
130
|
+
outputAs: 'class',
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Resolver
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
@Resolver('Author')
|
|
139
|
+
export class AuthorsResolver {
|
|
140
|
+
@Query('authors')
|
|
141
|
+
findAll() {
|
|
142
|
+
return this.authorsService.findAll();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Query('author')
|
|
146
|
+
findOne(@Args('id') id: string) {
|
|
147
|
+
return this.authorsService.findOne(id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Subscriptions
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { Subscription, Resolver } from '@nestjs/graphql';
|
|
156
|
+
import { PubSub } from 'graphql-subscriptions';
|
|
157
|
+
|
|
158
|
+
const pubSub = new PubSub();
|
|
159
|
+
|
|
160
|
+
@Resolver()
|
|
161
|
+
export class AuthorsResolver {
|
|
162
|
+
@Mutation(() => Author)
|
|
163
|
+
async createAuthor(@Args('input') input: CreateAuthorInput) {
|
|
164
|
+
const author = await this.authorsService.create(input);
|
|
165
|
+
pubSub.publish('authorCreated', { authorCreated: author });
|
|
166
|
+
return author;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@Subscription(() => Author)
|
|
170
|
+
authorCreated() {
|
|
171
|
+
return pubSub.asyncIterableIterator('authorCreated');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Field Resolvers
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
@Resolver(() => Author)
|
|
180
|
+
export class AuthorsResolver {
|
|
181
|
+
@ResolveField(() => [Post])
|
|
182
|
+
posts(@Parent() author: Author) {
|
|
183
|
+
return this.postsService.findByAuthorId(author.id);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Guards and Interceptors
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
@Query(() => Author)
|
|
192
|
+
@UseGuards(GqlAuthGuard)
|
|
193
|
+
whoAmI(@CurrentUser() user: User) {
|
|
194
|
+
return user;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Context
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
202
|
+
driver: ApolloDriver,
|
|
203
|
+
autoSchemaFile: true,
|
|
204
|
+
context: ({ req, res }) => ({ req, res }),
|
|
205
|
+
}),
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Async Configuration
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
GraphQLModule.forRootAsync<ApolloDriverConfig>({
|
|
212
|
+
driver: ApolloDriver,
|
|
213
|
+
imports: [ConfigModule],
|
|
214
|
+
useFactory: async (configService: ConfigService) => ({
|
|
215
|
+
autoSchemaFile: true,
|
|
216
|
+
playground: configService.get('NODE_ENV') !== 'production',
|
|
217
|
+
}),
|
|
218
|
+
inject: [ConfigService],
|
|
219
|
+
}),
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Key Points
|
|
223
|
+
|
|
224
|
+
- **Code-first**: TypeScript classes generate GraphQL schema
|
|
225
|
+
- **Schema-first**: SDL files generate TypeScript types
|
|
226
|
+
- Use `autoSchemaFile` for code-first
|
|
227
|
+
- Use `typePaths` for schema-first
|
|
228
|
+
- Resolvers must be registered as providers
|
|
229
|
+
|
|
230
|
+
<!--
|
|
231
|
+
Source references:
|
|
232
|
+
- https://docs.nestjs.com/graphql/quick-start
|
|
233
|
+
-->
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql-resolvers-mutations
|
|
3
|
+
description: GraphQL resolvers, mutations, and field resolvers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GraphQL Resolvers & Mutations
|
|
7
|
+
|
|
8
|
+
## Object Types (Code First)
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
|
|
12
|
+
|
|
13
|
+
@ObjectType()
|
|
14
|
+
export class Author {
|
|
15
|
+
@Field(() => ID)
|
|
16
|
+
id: string;
|
|
17
|
+
|
|
18
|
+
@Field()
|
|
19
|
+
name: string;
|
|
20
|
+
|
|
21
|
+
@Field(() => Int, { nullable: true })
|
|
22
|
+
age?: number;
|
|
23
|
+
|
|
24
|
+
@Field(() => [Post])
|
|
25
|
+
posts: Post[];
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Resolver
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Resolver, Query, Mutation, Args, ResolveField, Parent, ID } from '@nestjs/graphql';
|
|
33
|
+
|
|
34
|
+
@Resolver(() => Author)
|
|
35
|
+
export class AuthorsResolver {
|
|
36
|
+
constructor(
|
|
37
|
+
private authorsService: AuthorsService,
|
|
38
|
+
private postsService: PostsService,
|
|
39
|
+
) {}
|
|
40
|
+
|
|
41
|
+
@Query(() => [Author], { name: 'authors' })
|
|
42
|
+
findAll() {
|
|
43
|
+
return this.authorsService.findAll();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Query(() => Author, { nullable: true })
|
|
47
|
+
findOne(@Args('id', { type: () => ID }) id: string) {
|
|
48
|
+
return this.authorsService.findOne(id);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@ResolveField(() => [Post])
|
|
52
|
+
posts(@Parent() author: Author) {
|
|
53
|
+
return this.postsService.findByAuthorId(author.id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Mutations
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { InputType, Field, ArgsType } from '@nestjs/graphql';
|
|
62
|
+
|
|
63
|
+
@InputType()
|
|
64
|
+
export class CreateAuthorInput {
|
|
65
|
+
@Field()
|
|
66
|
+
name: string;
|
|
67
|
+
|
|
68
|
+
@Field({ nullable: true })
|
|
69
|
+
bio?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Resolver(() => Author)
|
|
73
|
+
export class AuthorsResolver {
|
|
74
|
+
@Mutation(() => Author)
|
|
75
|
+
createAuthor(@Args('input') input: CreateAuthorInput) {
|
|
76
|
+
return this.authorsService.create(input);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Mutation(() => Author)
|
|
80
|
+
updateAuthor(
|
|
81
|
+
@Args('id', { type: () => ID }) id: string,
|
|
82
|
+
@Args('input') input: UpdateAuthorInput,
|
|
83
|
+
) {
|
|
84
|
+
return this.authorsService.update(id, input);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Mutation(() => Boolean)
|
|
88
|
+
deleteAuthor(@Args('id', { type: () => ID }) id: string) {
|
|
89
|
+
return this.authorsService.delete(id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Args Class
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
@ArgsType()
|
|
98
|
+
class GetAuthorsArgs {
|
|
99
|
+
@Field(() => Int, { defaultValue: 0 })
|
|
100
|
+
offset: number;
|
|
101
|
+
|
|
102
|
+
@Field(() => Int, { defaultValue: 10 })
|
|
103
|
+
limit: number;
|
|
104
|
+
|
|
105
|
+
@Field({ nullable: true })
|
|
106
|
+
search?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Query(() => [Author])
|
|
110
|
+
authors(@Args() args: GetAuthorsArgs) {
|
|
111
|
+
return this.authorsService.findAll(args);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Field Options
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
@Field(() => String, {
|
|
119
|
+
description: 'The author name',
|
|
120
|
+
deprecationReason: 'Use fullName instead',
|
|
121
|
+
nullable: true,
|
|
122
|
+
})
|
|
123
|
+
name?: string;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Nullable Arrays
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// Array itself is nullable
|
|
130
|
+
@Field(() => [Post], { nullable: true })
|
|
131
|
+
posts?: Post[];
|
|
132
|
+
|
|
133
|
+
// Array items are nullable
|
|
134
|
+
@Field(() => [Post], { nullable: 'items' })
|
|
135
|
+
posts: (Post | null)[];
|
|
136
|
+
|
|
137
|
+
// Both nullable
|
|
138
|
+
@Field(() => [Post], { nullable: 'itemsAndList' })
|
|
139
|
+
posts?: (Post | null)[];
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## GraphQL Decorators
|
|
143
|
+
|
|
144
|
+
| Decorator | Purpose |
|
|
145
|
+
|-----------|---------|
|
|
146
|
+
| `@ObjectType()` | Define GraphQL type |
|
|
147
|
+
| `@Field()` | Define field |
|
|
148
|
+
| `@InputType()` | Define input type |
|
|
149
|
+
| `@ArgsType()` | Define args class |
|
|
150
|
+
| `@Resolver()` | Define resolver |
|
|
151
|
+
| `@Query()` | Define query |
|
|
152
|
+
| `@Mutation()` | Define mutation |
|
|
153
|
+
| `@ResolveField()` | Define field resolver |
|
|
154
|
+
| `@Parent()` | Access parent object |
|
|
155
|
+
| `@Args()` | Access arguments |
|
|
156
|
+
| `@Context()` | Access context |
|
|
157
|
+
|
|
158
|
+
## Query Options
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
@Query(() => Author, {
|
|
162
|
+
name: 'author', // GraphQL query name
|
|
163
|
+
description: 'Get author by ID',
|
|
164
|
+
nullable: true, // Can return null
|
|
165
|
+
deprecationReason: 'Use findAuthor instead',
|
|
166
|
+
})
|
|
167
|
+
async getAuthor(@Args('id', { type: () => ID }) id: string) {}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Context Access
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
@Query(() => Author)
|
|
174
|
+
whoAmI(@Context() context: any) {
|
|
175
|
+
return context.req.user;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Or extract specific property
|
|
179
|
+
@Query(() => Author)
|
|
180
|
+
whoAmI(@Context('req') req: Request) {
|
|
181
|
+
return req.user;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Using Guards
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
@Query(() => Author)
|
|
189
|
+
@UseGuards(GqlAuthGuard)
|
|
190
|
+
whoAmI(@Context() context: any) {
|
|
191
|
+
return context.req.user;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
<!--
|
|
196
|
+
Source references:
|
|
197
|
+
- https://docs.nestjs.com/graphql/resolvers-map
|
|
198
|
+
- https://docs.nestjs.com/graphql/mutations
|
|
199
|
+
-->
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql-scalars-unions-enums
|
|
3
|
+
description: GraphQL custom scalars, interfaces, union types, and enums
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GraphQL Scalars, Interfaces, Unions & Enums
|
|
7
|
+
|
|
8
|
+
## Interfaces
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { Field, ID, InterfaceType } from '@nestjs/graphql';
|
|
12
|
+
|
|
13
|
+
@InterfaceType()
|
|
14
|
+
export abstract class Character {
|
|
15
|
+
@Field(() => ID)
|
|
16
|
+
id: string;
|
|
17
|
+
|
|
18
|
+
@Field()
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Implement interface:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
@ObjectType({ implements: () => [Character] })
|
|
27
|
+
export class Human implements Character {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Custom `resolveType`:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
@InterfaceType({
|
|
37
|
+
resolveType(book) {
|
|
38
|
+
if (book.colors) return ColoringBook;
|
|
39
|
+
return TextBook;
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
export abstract class Book { ... }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Interface resolver (shared field resolvers):
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
@Resolver(() => Character)
|
|
49
|
+
export class CharacterInterfaceResolver {
|
|
50
|
+
@ResolveField(() => [Character])
|
|
51
|
+
friends(@Parent() character) {
|
|
52
|
+
return this.getFriends(character);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Scalars, Unions & Enums
|
|
58
|
+
|
|
59
|
+
## Built-in Scalars
|
|
60
|
+
|
|
61
|
+
- `ID`, `Int`, `Float`, `String`, `Boolean`
|
|
62
|
+
- `GraphQLISODateTime` (default for Date)
|
|
63
|
+
- `GraphQLTimestamp` (Date as epoch ms)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
GraphQLModule.forRoot({
|
|
67
|
+
buildSchemaOptions: {
|
|
68
|
+
dateScalarMode: 'timestamp', // Use GraphQLTimestamp for Date
|
|
69
|
+
numberScalarMode: 'integer', // Use Int for number
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Custom Scalar
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Scalar, CustomScalar } from '@nestjs/graphql';
|
|
78
|
+
import { Kind, ValueNode } from 'graphql';
|
|
79
|
+
|
|
80
|
+
@Scalar('Date', () => Date)
|
|
81
|
+
export class DateScalar implements CustomScalar<number, Date> {
|
|
82
|
+
description = 'Date custom scalar';
|
|
83
|
+
|
|
84
|
+
parseValue(value: number): Date {
|
|
85
|
+
return new Date(value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
serialize(value: Date): number {
|
|
89
|
+
return value.getTime();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
parseLiteral(ast: ValueNode): Date {
|
|
93
|
+
if (ast.kind === Kind.INT) return new Date(ast.value);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Register as provider. Use: `@Field(() => Date) creationDate: Date;`
|
|
100
|
+
|
|
101
|
+
## Import Scalar (e.g., JSON)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm i graphql-type-json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import GraphQLJSON from 'graphql-type-json';
|
|
109
|
+
|
|
110
|
+
GraphQLModule.forRoot({
|
|
111
|
+
resolvers: { JSON: GraphQLJSON },
|
|
112
|
+
}),
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Union Types
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { createUnionType } from '@nestjs/graphql';
|
|
119
|
+
|
|
120
|
+
export const ResultUnion = createUnionType({
|
|
121
|
+
name: 'ResultUnion',
|
|
122
|
+
types: () => [Author, Book] as const,
|
|
123
|
+
resolveType(value) {
|
|
124
|
+
if (value.name) return Author;
|
|
125
|
+
if (value.title) return Book;
|
|
126
|
+
return null;
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
@Query(() => [ResultUnion])
|
|
131
|
+
search(): Array<typeof ResultUnion> {
|
|
132
|
+
return [new Author(), new Book()];
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Return class instances for default `resolveType`. Schema first: add `__resolveType` resolver.
|
|
137
|
+
|
|
138
|
+
## Enums
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { registerEnumType } from '@nestjs/graphql';
|
|
142
|
+
|
|
143
|
+
export enum AllowedColor {
|
|
144
|
+
RED,
|
|
145
|
+
GREEN,
|
|
146
|
+
BLUE,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
registerEnumType(AllowedColor, {
|
|
150
|
+
name: 'AllowedColor',
|
|
151
|
+
description: 'Supported colors',
|
|
152
|
+
valuesMap: {
|
|
153
|
+
RED: { description: 'Default color' },
|
|
154
|
+
BLUE: { deprecationReason: 'Too blue' },
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
@Field(() => AllowedColor)
|
|
161
|
+
favoriteColor: AllowedColor;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Schema first: define in SDL; use `resolvers` for internal value mapping.
|
|
165
|
+
|
|
166
|
+
## Key Points
|
|
167
|
+
|
|
168
|
+
- Use abstract class with `@InterfaceType()` (not TypeScript interface)
|
|
169
|
+
- `implements: () => [Character]` for ObjectType implementing interface
|
|
170
|
+
- Use `as const` for union types array
|
|
171
|
+
- Custom scalars need `parseValue`, `serialize`, `parseLiteral`
|
|
172
|
+
- `graphql-scalars` package for common scalars (UUID, Email, etc.)
|
|
173
|
+
- Enum internal values: use resolver object in schema-first
|
|
174
|
+
|
|
175
|
+
<!--
|
|
176
|
+
Source references:
|
|
177
|
+
- https://docs.nestjs.com/graphql/interfaces
|
|
178
|
+
- https://docs.nestjs.com/graphql/scalars
|
|
179
|
+
- https://docs.nestjs.com/graphql/unions-and-enums
|
|
180
|
+
-->
|