@biggora/claude-plugins 1.2.2 → 1.3.1
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 +5 -1
- package/package.json +1 -1
- package/registry/registry.json +15 -0
- package/specs/coding.md +11 -0
- package/src/commands/skills/add.js +115 -31
- package/src/commands/skills/list.js +25 -52
- package/src/commands/skills/remove.js +45 -27
- package/src/commands/skills/resolve.js +104 -0
- package/src/commands/skills/update.js +58 -74
- package/src/config.js +5 -0
- 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,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-dynamic-modules
|
|
3
|
+
description: Creating configurable dynamic modules in NestJS
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Dynamic Modules
|
|
7
|
+
|
|
8
|
+
Dynamic modules allow you to create modules that can be configured at runtime, providing flexible and customizable module APIs.
|
|
9
|
+
|
|
10
|
+
## Static vs Dynamic Modules
|
|
11
|
+
|
|
12
|
+
Static modules have fixed configuration:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
@Module({
|
|
16
|
+
imports: [ConfigModule],
|
|
17
|
+
})
|
|
18
|
+
export class AppModule {}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Dynamic modules accept configuration:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
@Module({
|
|
25
|
+
imports: [ConfigModule.forRoot({ envFilePath: '.env' })],
|
|
26
|
+
})
|
|
27
|
+
export class AppModule {}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Creating Dynamic Modules
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Module, DynamicModule } from '@nestjs/common';
|
|
34
|
+
|
|
35
|
+
@Module({
|
|
36
|
+
providers: [Connection],
|
|
37
|
+
exports: [Connection],
|
|
38
|
+
})
|
|
39
|
+
export class DatabaseModule {
|
|
40
|
+
static forRoot(entities = [], options?): DynamicModule {
|
|
41
|
+
const providers = createDatabaseProviders(options, entities);
|
|
42
|
+
return {
|
|
43
|
+
module: DatabaseModule,
|
|
44
|
+
providers: providers,
|
|
45
|
+
exports: providers,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Using Dynamic Modules
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
@Module({
|
|
55
|
+
imports: [DatabaseModule.forRoot([User])],
|
|
56
|
+
})
|
|
57
|
+
export class AppModule {}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Global Dynamic Modules
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
{
|
|
64
|
+
global: true,
|
|
65
|
+
module: DatabaseModule,
|
|
66
|
+
providers: providers,
|
|
67
|
+
exports: providers,
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Re-exporting Dynamic Modules
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
@Module({
|
|
75
|
+
imports: [DatabaseModule.forRoot([User])],
|
|
76
|
+
exports: [DatabaseModule],
|
|
77
|
+
})
|
|
78
|
+
export class AppModule {}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Async Dynamic Modules
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
static forRootAsync(options: ConfigModuleAsyncOptions): DynamicModule {
|
|
85
|
+
return {
|
|
86
|
+
module: ConfigModule,
|
|
87
|
+
imports: options.imports || [],
|
|
88
|
+
providers: [
|
|
89
|
+
{
|
|
90
|
+
provide: CONFIG_OPTIONS,
|
|
91
|
+
useFactory: options.useFactory,
|
|
92
|
+
inject: options.inject || [],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
exports: [ConfigService],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## ConfigurableModuleBuilder
|
|
101
|
+
|
|
102
|
+
Use `ConfigurableModuleBuilder` for advanced scenarios:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { ConfigurableModuleBuilder } from '@nestjs/common';
|
|
106
|
+
|
|
107
|
+
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
|
|
108
|
+
new ConfigurableModuleBuilder<ConfigModuleOptions>()
|
|
109
|
+
.setClassMethodName('forRoot')
|
|
110
|
+
.build();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Key Points
|
|
114
|
+
|
|
115
|
+
- Dynamic modules provide runtime configuration
|
|
116
|
+
- Use static `forRoot()` or `forRootAsync()` methods
|
|
117
|
+
- Return `DynamicModule` from factory methods
|
|
118
|
+
- Dynamic module properties extend base module metadata
|
|
119
|
+
- Use `ConfigurableModuleBuilder` for complex scenarios
|
|
120
|
+
- Dynamic modules can be global or scoped
|
|
121
|
+
|
|
122
|
+
<!--
|
|
123
|
+
Source references:
|
|
124
|
+
- https://docs.nestjs.com/fundamentals/dynamic-modules
|
|
125
|
+
-->
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-exception-filters
|
|
3
|
+
description: NestJS exception filters for error handling
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exception Filters
|
|
7
|
+
|
|
8
|
+
Exception filters give you full control over the exceptions layer and the response sent back to the client.
|
|
9
|
+
|
|
10
|
+
## Throwing Exceptions
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
@Get()
|
|
14
|
+
async findAll() {
|
|
15
|
+
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Response:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"statusCode": 403,
|
|
24
|
+
"message": "Forbidden"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Custom Response Body
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
throw new HttpException({
|
|
32
|
+
status: HttpStatus.FORBIDDEN,
|
|
33
|
+
error: 'This is a custom message',
|
|
34
|
+
}, HttpStatus.FORBIDDEN);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Built-in HTTP Exceptions
|
|
38
|
+
|
|
39
|
+
- `BadRequestException`
|
|
40
|
+
- `UnauthorizedException`
|
|
41
|
+
- `NotFoundException`
|
|
42
|
+
- `ForbiddenException`
|
|
43
|
+
- `NotAcceptableException`
|
|
44
|
+
- `RequestTimeoutException`
|
|
45
|
+
- `ConflictException`
|
|
46
|
+
- `GoneException`
|
|
47
|
+
- `HttpVersionNotSupportedException`
|
|
48
|
+
- `PayloadTooLargeException`
|
|
49
|
+
- `UnsupportedMediaTypeException`
|
|
50
|
+
- `UnprocessableEntityException`
|
|
51
|
+
- `InternalServerErrorException`
|
|
52
|
+
- `NotImplementedException`
|
|
53
|
+
- `ImATeapotException`
|
|
54
|
+
- `MethodNotAllowedException`
|
|
55
|
+
- `BadGatewayException`
|
|
56
|
+
- `ServiceUnavailableException`
|
|
57
|
+
- `GatewayTimeoutException`
|
|
58
|
+
- `PreconditionFailedException`
|
|
59
|
+
|
|
60
|
+
## Custom Exception Filter
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
|
|
64
|
+
import { Request, Response } from 'express';
|
|
65
|
+
|
|
66
|
+
@Catch(HttpException)
|
|
67
|
+
export class HttpExceptionFilter implements ExceptionFilter {
|
|
68
|
+
catch(exception: HttpException, host: ArgumentsHost) {
|
|
69
|
+
const ctx = host.switchToHttp();
|
|
70
|
+
const response = ctx.getResponse<Response>();
|
|
71
|
+
const request = ctx.getRequest<Request>();
|
|
72
|
+
const status = exception.getStatus();
|
|
73
|
+
|
|
74
|
+
response
|
|
75
|
+
.status(status)
|
|
76
|
+
.json({
|
|
77
|
+
statusCode: status,
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
path: request.url,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Binding Filters
|
|
86
|
+
|
|
87
|
+
### Method-scoped
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
@Post()
|
|
91
|
+
@UseFilters(new HttpExceptionFilter())
|
|
92
|
+
async create(@Body() createCatDto: CreateCatDto) {
|
|
93
|
+
throw new ForbiddenException();
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Controller-scoped
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
@Controller()
|
|
101
|
+
@UseFilters(new HttpExceptionFilter())
|
|
102
|
+
export class CatsController {}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Global Filters
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const app = await NestFactory.create(AppModule);
|
|
109
|
+
app.useGlobalFilters(new HttpExceptionFilter());
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or via module:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { Module } from '@nestjs/common';
|
|
116
|
+
import { APP_FILTER } from '@nestjs/core';
|
|
117
|
+
|
|
118
|
+
@Module({
|
|
119
|
+
providers: [
|
|
120
|
+
{
|
|
121
|
+
provide: APP_FILTER,
|
|
122
|
+
useClass: HttpExceptionFilter,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
})
|
|
126
|
+
export class AppModule {}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Catching Everything
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
@Catch()
|
|
133
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
134
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
135
|
+
// Handle all exceptions
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Platform-agnostic Filter
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import {
|
|
144
|
+
ExceptionFilter,
|
|
145
|
+
Catch,
|
|
146
|
+
ArgumentsHost,
|
|
147
|
+
HttpException,
|
|
148
|
+
HttpStatus,
|
|
149
|
+
} from '@nestjs/common';
|
|
150
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
151
|
+
|
|
152
|
+
@Catch()
|
|
153
|
+
export class CatchEverythingFilter implements ExceptionFilter {
|
|
154
|
+
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
|
|
155
|
+
|
|
156
|
+
catch(exception: unknown, host: ArgumentsHost): void {
|
|
157
|
+
const { httpAdapter } = this.httpAdapterHost;
|
|
158
|
+
const ctx = host.switchToHttp();
|
|
159
|
+
|
|
160
|
+
const httpStatus =
|
|
161
|
+
exception instanceof HttpException
|
|
162
|
+
? exception.getStatus()
|
|
163
|
+
: HttpStatus.INTERNAL_SERVER_ERROR;
|
|
164
|
+
|
|
165
|
+
const responseBody = {
|
|
166
|
+
statusCode: httpStatus,
|
|
167
|
+
timestamp: new Date().toISOString(),
|
|
168
|
+
path: httpAdapter.getRequestUrl(ctx.getRequest()),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Extending Base Filter
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { Catch, ArgumentsHost } from '@nestjs/common';
|
|
180
|
+
import { BaseExceptionFilter } from '@nestjs/core';
|
|
181
|
+
|
|
182
|
+
@Catch()
|
|
183
|
+
export class AllExceptionsFilter extends BaseExceptionFilter {
|
|
184
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
185
|
+
super.catch(exception, host);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Key Points
|
|
191
|
+
|
|
192
|
+
- Filters catch exceptions and format responses
|
|
193
|
+
- Use `@Catch()` to specify exception types
|
|
194
|
+
- Filters can be method, controller, or global scoped
|
|
195
|
+
- Use `HttpAdapterHost` for platform-agnostic code
|
|
196
|
+
- Extend `BaseExceptionFilter` to reuse default behavior
|
|
197
|
+
- Multiple filters can be chained
|
|
198
|
+
|
|
199
|
+
<!--
|
|
200
|
+
Source references:
|
|
201
|
+
- https://docs.nestjs.com/exception-filters
|
|
202
|
+
-->
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-execution-context
|
|
3
|
+
description: Accessing execution context in NestJS guards, filters, and interceptors
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Execution Context
|
|
7
|
+
|
|
8
|
+
`ExecutionContext` provides information about the current execution context, useful for building generic guards, filters, and interceptors.
|
|
9
|
+
|
|
10
|
+
## ArgumentsHost
|
|
11
|
+
|
|
12
|
+
`ArgumentsHost` provides methods to retrieve handler arguments:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const ctx = host.switchToHttp();
|
|
16
|
+
const request = ctx.getRequest<Request>();
|
|
17
|
+
const response = ctx.getResponse<Response>();
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Application Context Types
|
|
21
|
+
|
|
22
|
+
Determine application type:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
if (host.getType() === 'http') {
|
|
26
|
+
// HTTP context
|
|
27
|
+
} else if (host.getType() === 'rpc') {
|
|
28
|
+
// Microservice context
|
|
29
|
+
} else if (host.getType<GqlContextType>() === 'graphql') {
|
|
30
|
+
// GraphQL context
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Switching Contexts
|
|
35
|
+
|
|
36
|
+
### HTTP Context
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const ctx = host.switchToHttp();
|
|
40
|
+
const request = ctx.getRequest<Request>();
|
|
41
|
+
const response = ctx.getResponse<Response>();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### WebSocket Context
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const ctx = host.switchToWs();
|
|
48
|
+
const client = ctx.getClient<Socket>();
|
|
49
|
+
const data = ctx.getData();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### RPC Context
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const ctx = host.switchToRpc();
|
|
56
|
+
const data = ctx.getData();
|
|
57
|
+
const context = ctx.getContext();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## ExecutionContext
|
|
61
|
+
|
|
62
|
+
`ExecutionContext` extends `ArgumentsHost` with additional methods:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
export interface ExecutionContext extends ArgumentsHost {
|
|
66
|
+
getClass<T = any>(): Type<T>;
|
|
67
|
+
getHandler(): Function;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Using in Guards
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
@Injectable()
|
|
75
|
+
export class RolesGuard implements CanActivate {
|
|
76
|
+
constructor(private reflector: Reflector) {}
|
|
77
|
+
|
|
78
|
+
canActivate(context: ExecutionContext): boolean {
|
|
79
|
+
const roles = this.reflector.get(Roles, context.getHandler());
|
|
80
|
+
const request = context.switchToHttp().getRequest();
|
|
81
|
+
return matchRoles(roles, request.user.roles);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Reflection and Metadata
|
|
87
|
+
|
|
88
|
+
Access route metadata:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const roles = this.reflector.get(Roles, context.getHandler());
|
|
92
|
+
const roles = this.reflector.get(Roles, context.getClass());
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Key Points
|
|
96
|
+
|
|
97
|
+
- `ArgumentsHost` provides access to handler arguments
|
|
98
|
+
- `ExecutionContext` adds class and handler information
|
|
99
|
+
- Use `switchToHttp()`, `switchToWs()`, `switchToRpc()` for context-specific access
|
|
100
|
+
- Use `getType()` to determine application context
|
|
101
|
+
- `Reflector` provides access to route metadata
|
|
102
|
+
- Execution context is available in guards, filters, and interceptors
|
|
103
|
+
|
|
104
|
+
<!--
|
|
105
|
+
Source references:
|
|
106
|
+
- https://docs.nestjs.com/fundamentals/execution-context
|
|
107
|
+
-->
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-guards
|
|
3
|
+
description: NestJS guards for authorization and access control
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Guards
|
|
7
|
+
|
|
8
|
+
Guards determine whether a request should be handled by a route handler. They have access to `ExecutionContext` and know what will be executed next.
|
|
9
|
+
|
|
10
|
+
## Basic Guard
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
14
|
+
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class AuthGuard implements CanActivate {
|
|
17
|
+
canActivate(
|
|
18
|
+
context: ExecutionContext,
|
|
19
|
+
): boolean | Promise<boolean> | Observable<boolean> {
|
|
20
|
+
const request = context.switchToHttp().getRequest();
|
|
21
|
+
return validateRequest(request);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Role-based Guard
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
30
|
+
import { Reflector } from '@nestjs/core';
|
|
31
|
+
import { Roles } from './roles.decorator';
|
|
32
|
+
|
|
33
|
+
@Injectable()
|
|
34
|
+
export class RolesGuard implements CanActivate {
|
|
35
|
+
constructor(private reflector: Reflector) {}
|
|
36
|
+
|
|
37
|
+
canActivate(context: ExecutionContext): boolean {
|
|
38
|
+
const roles = this.reflector.get(Roles, context.getHandler());
|
|
39
|
+
if (!roles) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const request = context.switchToHttp().getRequest();
|
|
43
|
+
const user = request.user;
|
|
44
|
+
return matchRoles(roles, user.roles);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Custom Decorator
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Reflector } from '@nestjs/core';
|
|
53
|
+
|
|
54
|
+
export const Roles = Reflector.createDecorator<string[]>();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
@Post()
|
|
61
|
+
@Roles(['admin'])
|
|
62
|
+
async create(@Body() createCatDto: CreateCatDto) {
|
|
63
|
+
this.catsService.create(createCatDto);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Binding Guards
|
|
68
|
+
|
|
69
|
+
### Controller-scoped
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
@Controller('cats')
|
|
73
|
+
@UseGuards(RolesGuard)
|
|
74
|
+
export class CatsController {}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Method-scoped
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
@Post()
|
|
81
|
+
@UseGuards(RolesGuard)
|
|
82
|
+
async create(@Body() createCatDto: CreateCatDto) {
|
|
83
|
+
this.catsService.create(createCatDto);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Global Guards
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const app = await NestFactory.create(AppModule);
|
|
91
|
+
app.useGlobalGuards(new RolesGuard());
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Or via module:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { Module } from '@nestjs/common';
|
|
98
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
99
|
+
|
|
100
|
+
@Module({
|
|
101
|
+
providers: [
|
|
102
|
+
{
|
|
103
|
+
provide: APP_GUARD,
|
|
104
|
+
useClass: RolesGuard,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
})
|
|
108
|
+
export class AppModule {}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Multiple Guards
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
@UseGuards(AuthGuard, RolesGuard)
|
|
115
|
+
export class CatsController {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Execution Order
|
|
119
|
+
|
|
120
|
+
Guards are executed:
|
|
121
|
+
- After middleware
|
|
122
|
+
- Before interceptors and pipes
|
|
123
|
+
|
|
124
|
+
## Key Points
|
|
125
|
+
|
|
126
|
+
- Guards return `true` to allow, `false` to deny
|
|
127
|
+
- Guards can be async (return `Promise<boolean>`)
|
|
128
|
+
- Use `Reflector` to access route metadata
|
|
129
|
+
- Guards throw exceptions to deny access
|
|
130
|
+
- Use `@SetMetadata()` or `Reflector.createDecorator()` for custom metadata
|
|
131
|
+
- Guards have access to `ExecutionContext`
|
|
132
|
+
|
|
133
|
+
<!--
|
|
134
|
+
Source references:
|
|
135
|
+
- https://docs.nestjs.com/guards
|
|
136
|
+
-->
|