@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,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lifecycle-events
|
|
3
|
+
description: Application and provider lifecycle hooks in NestJS
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lifecycle Events
|
|
7
|
+
|
|
8
|
+
NestJS provides lifecycle hooks that give visibility into key application events and allow running code when they occur.
|
|
9
|
+
|
|
10
|
+
## Lifecycle Sequence
|
|
11
|
+
|
|
12
|
+
The lifecycle consists of three phases: **initializing**, **running**, and **terminating**.
|
|
13
|
+
|
|
14
|
+
## Lifecycle Hooks
|
|
15
|
+
|
|
16
|
+
| Hook | When Triggered |
|
|
17
|
+
|------|----------------|
|
|
18
|
+
| `onModuleInit()` | Called once the host module's dependencies have been resolved |
|
|
19
|
+
| `onApplicationBootstrap()` | Called once all modules have been initialized, but before listening |
|
|
20
|
+
| `onModuleDestroy()` | Called after a termination signal (e.g., SIGTERM) has been received |
|
|
21
|
+
| `beforeApplicationShutdown()` | Called after all `onModuleDestroy()` handlers have completed |
|
|
22
|
+
| `onApplicationShutdown()` | Called after connections close (`app.close()` resolves) |
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Implement the appropriate interface to register a lifecycle hook:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Injectable, OnModuleInit, OnApplicationBootstrap } from '@nestjs/common';
|
|
30
|
+
|
|
31
|
+
@Injectable()
|
|
32
|
+
export class UsersService implements OnModuleInit, OnApplicationBootstrap {
|
|
33
|
+
onModuleInit() {
|
|
34
|
+
console.log('Module initialized');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onApplicationBootstrap() {
|
|
38
|
+
console.log('Application bootstrapped');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Asynchronous Initialization
|
|
44
|
+
|
|
45
|
+
Hooks can be async - Nest will wait for the promise to resolve:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
async onModuleInit(): Promise<void> {
|
|
49
|
+
await this.fetchConfiguration();
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Enabling Shutdown Hooks
|
|
54
|
+
|
|
55
|
+
Shutdown hooks are disabled by default. Enable them in `main.ts`:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
async function bootstrap() {
|
|
59
|
+
const app = await NestFactory.create(AppModule);
|
|
60
|
+
app.enableShutdownHooks();
|
|
61
|
+
await app.listen(3000);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Handle shutdown signals:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
@Injectable()
|
|
69
|
+
class DatabaseService implements OnApplicationShutdown {
|
|
70
|
+
onApplicationShutdown(signal: string) {
|
|
71
|
+
console.log(`Received ${signal}, closing connections...`);
|
|
72
|
+
// Cleanup logic
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Key Points
|
|
78
|
+
|
|
79
|
+
- Lifecycle hooks are not triggered for **request-scoped** providers
|
|
80
|
+
- Execution order depends on module import order
|
|
81
|
+
- Windows has limited shutdown hook support (SIGINT works, SIGTERM doesn't)
|
|
82
|
+
- `enableShutdownHooks` consumes memory by starting listeners
|
|
83
|
+
|
|
84
|
+
<!--
|
|
85
|
+
Source references:
|
|
86
|
+
- https://docs.nestjs.com/fundamentals/lifecycle-events
|
|
87
|
+
-->
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: module-reference
|
|
3
|
+
description: Accessing providers dynamically with ModuleRef
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Module Reference
|
|
7
|
+
|
|
8
|
+
`ModuleRef` provides methods to navigate the DI container and obtain references to providers dynamically.
|
|
9
|
+
|
|
10
|
+
## Basic Usage
|
|
11
|
+
|
|
12
|
+
Inject `ModuleRef` to access providers:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
16
|
+
import { ModuleRef } from '@nestjs/core';
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class CatsService implements OnModuleInit {
|
|
20
|
+
private service: Service;
|
|
21
|
+
|
|
22
|
+
constructor(private moduleRef: ModuleRef) {}
|
|
23
|
+
|
|
24
|
+
onModuleInit() {
|
|
25
|
+
this.service = this.moduleRef.get(Service);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Retrieving Instances
|
|
31
|
+
|
|
32
|
+
### Static Instances (`get()`)
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// From current module
|
|
36
|
+
const service = this.moduleRef.get(Service);
|
|
37
|
+
|
|
38
|
+
// From global context (different module)
|
|
39
|
+
const service = this.moduleRef.get(Service, { strict: false });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Scoped Providers (`resolve()`)
|
|
43
|
+
|
|
44
|
+
For transient or request-scoped providers:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const transientService = await this.moduleRef.resolve(TransientService);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Each `resolve()` call returns a unique instance. To get the same instance, pass a context identifier:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { ContextIdFactory } from '@nestjs/core';
|
|
54
|
+
|
|
55
|
+
const contextId = ContextIdFactory.create();
|
|
56
|
+
const service1 = await this.moduleRef.resolve(TransientService, contextId);
|
|
57
|
+
const service2 = await this.moduleRef.resolve(TransientService, contextId);
|
|
58
|
+
// service1 === service2
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Request Context
|
|
62
|
+
|
|
63
|
+
Get the context ID from an existing request:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
@Injectable()
|
|
67
|
+
export class CatsService {
|
|
68
|
+
constructor(
|
|
69
|
+
@Inject(REQUEST) private request: Record<string, unknown>,
|
|
70
|
+
private moduleRef: ModuleRef,
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
async getRepository() {
|
|
74
|
+
const contextId = ContextIdFactory.getByRequest(this.request);
|
|
75
|
+
return this.moduleRef.resolve(CatsRepository, contextId);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Dynamic Instantiation
|
|
81
|
+
|
|
82
|
+
Create instances of classes not registered as providers:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const factory = await this.moduleRef.create(CatsFactory);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Register REQUEST Provider
|
|
89
|
+
|
|
90
|
+
For manually created DI sub-trees:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const contextId = ContextIdFactory.create();
|
|
94
|
+
this.moduleRef.registerRequestByContextId(myRequestObject, contextId);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Key Points
|
|
98
|
+
|
|
99
|
+
- `get()` only retrieves static (singleton) instances
|
|
100
|
+
- `resolve()` is async and for scoped providers
|
|
101
|
+
- Use `{ strict: false }` to access providers from other modules
|
|
102
|
+
- `ModuleRef` is imported from `@nestjs/core`
|
|
103
|
+
|
|
104
|
+
<!--
|
|
105
|
+
Source references:
|
|
106
|
+
- https://docs.nestjs.com/fundamentals/module-ref
|
|
107
|
+
-->
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-pipes
|
|
3
|
+
description: NestJS pipes for data transformation and validation
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pipes
|
|
7
|
+
|
|
8
|
+
Pipes transform input data to the desired form or validate input data. They operate on arguments before route handlers are invoked.
|
|
9
|
+
|
|
10
|
+
## Built-in Pipes
|
|
11
|
+
|
|
12
|
+
- `ValidationPipe` - Validates DTOs
|
|
13
|
+
- `ParseIntPipe` - Parses strings to integers
|
|
14
|
+
- `ParseFloatPipe` - Parses strings to floats
|
|
15
|
+
- `ParseBoolPipe` - Parses strings to booleans
|
|
16
|
+
- `ParseArrayPipe` - Parses strings to arrays
|
|
17
|
+
- `ParseUUIDPipe` - Validates UUIDs
|
|
18
|
+
- `ParseEnumPipe` - Validates enum values
|
|
19
|
+
- `DefaultValuePipe` - Provides default values
|
|
20
|
+
- `ParseFilePipe` - Validates file uploads
|
|
21
|
+
- `ParseDatePipe` - Parses date strings
|
|
22
|
+
|
|
23
|
+
## Binding Pipes
|
|
24
|
+
|
|
25
|
+
### Parameter-level Binding
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
@Get(':id')
|
|
29
|
+
async findOne(@Param('id', ParseIntPipe) id: number) {
|
|
30
|
+
return this.catsService.findOne(id);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### With Options
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
@Get(':id')
|
|
38
|
+
async findOne(
|
|
39
|
+
@Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
|
|
40
|
+
id: number,
|
|
41
|
+
) {
|
|
42
|
+
return this.catsService.findOne(id);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Query Parameters
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
@Get()
|
|
50
|
+
async findOne(@Query('id', ParseIntPipe) id: number) {
|
|
51
|
+
return this.catsService.findOne(id);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Custom Pipes
|
|
56
|
+
|
|
57
|
+
### Basic Pipe
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
|
|
61
|
+
|
|
62
|
+
@Injectable()
|
|
63
|
+
export class ValidationPipe implements PipeTransform {
|
|
64
|
+
transform(value: any, metadata: ArgumentMetadata) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Validation Pipe with Zod
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { PipeTransform, ArgumentMetadata, BadRequestException } from '@nestjs/common';
|
|
74
|
+
import { ZodSchema } from 'zod';
|
|
75
|
+
|
|
76
|
+
export class ZodValidationPipe implements PipeTransform {
|
|
77
|
+
constructor(private schema: ZodSchema) {}
|
|
78
|
+
|
|
79
|
+
transform(value: unknown, metadata: ArgumentMetadata) {
|
|
80
|
+
try {
|
|
81
|
+
const parsedValue = this.schema.parse(value);
|
|
82
|
+
return parsedValue;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new BadRequestException('Validation failed');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Usage:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
@Post()
|
|
94
|
+
@UsePipes(new ZodValidationPipe(createCatSchema))
|
|
95
|
+
async create(@Body() createCatDto: CreateCatDto) {
|
|
96
|
+
this.catsService.create(createCatDto);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Validation Pipe with class-validator
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
|
|
104
|
+
import { validate } from 'class-validator';
|
|
105
|
+
import { plainToInstance } from 'class-transformer';
|
|
106
|
+
|
|
107
|
+
@Injectable()
|
|
108
|
+
export class ValidationPipe implements PipeTransform<any> {
|
|
109
|
+
async transform(value: any, { metatype }: ArgumentMetadata) {
|
|
110
|
+
if (!metatype || !this.toValidate(metatype)) {
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
const object = plainToInstance(metatype, value);
|
|
114
|
+
const errors = await validate(object);
|
|
115
|
+
if (errors.length > 0) {
|
|
116
|
+
throw new BadRequestException('Validation failed');
|
|
117
|
+
}
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private toValidate(metatype: Function): boolean {
|
|
122
|
+
const types: Function[] = [String, Boolean, Number, Array, Object];
|
|
123
|
+
return !types.includes(metatype);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Transformation Pipe
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
|
|
132
|
+
|
|
133
|
+
@Injectable()
|
|
134
|
+
export class ParseIntPipe implements PipeTransform<string, number> {
|
|
135
|
+
transform(value: string, metadata: ArgumentMetadata): number {
|
|
136
|
+
const val = parseInt(value, 10);
|
|
137
|
+
if (isNaN(val)) {
|
|
138
|
+
throw new BadRequestException('Validation failed');
|
|
139
|
+
}
|
|
140
|
+
return val;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Global Pipes
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
async function bootstrap() {
|
|
149
|
+
const app = await NestFactory.create(AppModule);
|
|
150
|
+
app.useGlobalPipes(new ValidationPipe());
|
|
151
|
+
await app.listen(3000);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Or via module:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { Module } from '@nestjs/common';
|
|
159
|
+
import { APP_PIPE } from '@nestjs/core';
|
|
160
|
+
|
|
161
|
+
@Module({
|
|
162
|
+
providers: [
|
|
163
|
+
{
|
|
164
|
+
provide: APP_PIPE,
|
|
165
|
+
useClass: ValidationPipe,
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
})
|
|
169
|
+
export class AppModule {}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Default Values
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
@Get()
|
|
176
|
+
async findAll(
|
|
177
|
+
@Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
|
|
178
|
+
@Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
|
|
179
|
+
) {
|
|
180
|
+
return this.catsService.findAll({ activeOnly, page });
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Key Points
|
|
185
|
+
|
|
186
|
+
- Pipes run before route handlers
|
|
187
|
+
- Pipes can transform or validate data
|
|
188
|
+
- Use built-in `ValidationPipe` for DTO validation
|
|
189
|
+
- Pipes can be parameter, method, controller, or global scoped
|
|
190
|
+
- Pipes throw exceptions to stop execution
|
|
191
|
+
- Use `DefaultValuePipe` before `Parse*` pipes for optional parameters
|
|
192
|
+
|
|
193
|
+
<!--
|
|
194
|
+
Source references:
|
|
195
|
+
- https://docs.nestjs.com/pipes
|
|
196
|
+
- https://docs.nestjs.com/techniques/validation
|
|
197
|
+
-->
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fundamentals-provider-scopes
|
|
3
|
+
description: Provider scopes in NestJS (singleton, request, transient)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Provider Scopes
|
|
7
|
+
|
|
8
|
+
Providers can have different lifetimes (scopes) that determine how instances are created and shared.
|
|
9
|
+
|
|
10
|
+
## Scope Types
|
|
11
|
+
|
|
12
|
+
- `DEFAULT` - Singleton, shared across entire application
|
|
13
|
+
- `REQUEST` - New instance per request
|
|
14
|
+
- `TRANSIENT` - New instance per consumer
|
|
15
|
+
|
|
16
|
+
## Default Scope (Singleton)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
@Injectable()
|
|
20
|
+
export class CatsService {}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Singleton is the default and recommended scope.
|
|
24
|
+
|
|
25
|
+
## Request Scope
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Injectable, Scope } from '@nestjs/common';
|
|
29
|
+
|
|
30
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
31
|
+
export class CatsService {}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Transient Scope
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
@Injectable({ scope: Scope.TRANSIENT })
|
|
38
|
+
export class LoggerService {}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Custom Provider Scopes
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
{
|
|
45
|
+
provide: 'CACHE_MANAGER',
|
|
46
|
+
useClass: CacheManager,
|
|
47
|
+
scope: Scope.TRANSIENT,
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Controller Scope
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
@Controller({
|
|
55
|
+
path: 'cats',
|
|
56
|
+
scope: Scope.REQUEST,
|
|
57
|
+
})
|
|
58
|
+
export class CatsController {}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Accessing Request Object
|
|
62
|
+
|
|
63
|
+
Inject `REQUEST` to access request in request-scoped providers:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { Injectable, Scope, Inject } from '@nestjs/common';
|
|
67
|
+
import { REQUEST } from '@nestjs/core';
|
|
68
|
+
import { Request } from 'express';
|
|
69
|
+
|
|
70
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
71
|
+
export class CatsService {
|
|
72
|
+
constructor(@Inject(REQUEST) private request: Request) {}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Scope Hierarchy
|
|
77
|
+
|
|
78
|
+
Request scope bubbles up the injection chain. If a controller depends on a request-scoped provider, the controller becomes request-scoped.
|
|
79
|
+
|
|
80
|
+
## Key Points
|
|
81
|
+
|
|
82
|
+
- Singleton scope is default and recommended
|
|
83
|
+
- Request scope creates new instance per request
|
|
84
|
+
- Transient scope creates new instance per consumer
|
|
85
|
+
- Request scope propagates up dependency chain
|
|
86
|
+
- Use `REQUEST` token to access request object
|
|
87
|
+
- WebSocket gateways should not use request-scoped providers
|
|
88
|
+
|
|
89
|
+
<!--
|
|
90
|
+
Source references:
|
|
91
|
+
- https://docs.nestjs.com/fundamentals/injection-scopes
|
|
92
|
+
-->
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing
|
|
3
|
+
description: Unit testing and e2e testing in NestJS with @nestjs/testing
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing
|
|
7
|
+
|
|
8
|
+
NestJS provides utilities for unit and end-to-end testing, with built-in Jest and Supertest integration.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i --save-dev @nestjs/testing
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Unit Testing
|
|
17
|
+
|
|
18
|
+
### Basic Test Setup
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { Test } from '@nestjs/testing';
|
|
22
|
+
import { CatsController } from './cats.controller';
|
|
23
|
+
import { CatsService } from './cats.service';
|
|
24
|
+
|
|
25
|
+
describe('CatsController', () => {
|
|
26
|
+
let controller: CatsController;
|
|
27
|
+
let service: CatsService;
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
const moduleRef = await Test.createTestingModule({
|
|
31
|
+
controllers: [CatsController],
|
|
32
|
+
providers: [CatsService],
|
|
33
|
+
}).compile();
|
|
34
|
+
|
|
35
|
+
service = moduleRef.get(CatsService);
|
|
36
|
+
controller = moduleRef.get(CatsController);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return cats', async () => {
|
|
40
|
+
const result = ['cat'];
|
|
41
|
+
jest.spyOn(service, 'findAll').mockImplementation(() => result);
|
|
42
|
+
expect(await controller.findAll()).toBe(result);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Auto Mocking
|
|
48
|
+
|
|
49
|
+
Use `useMocker()` to automatically mock dependencies:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const moduleRef = await Test.createTestingModule({
|
|
53
|
+
controllers: [CatsController],
|
|
54
|
+
})
|
|
55
|
+
.useMocker((token) => {
|
|
56
|
+
if (token === CatsService) {
|
|
57
|
+
return { findAll: jest.fn().mockResolvedValue(['cat']) };
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.compile();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Overriding Providers
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const moduleRef = await Test.createTestingModule({
|
|
67
|
+
imports: [CatsModule],
|
|
68
|
+
})
|
|
69
|
+
.overrideProvider(CatsService)
|
|
70
|
+
.useValue(mockCatsService)
|
|
71
|
+
.compile();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## End-to-End Testing
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import * as request from 'supertest';
|
|
78
|
+
import { Test } from '@nestjs/testing';
|
|
79
|
+
import { CatsModule } from '../src/cats/cats.module';
|
|
80
|
+
import { INestApplication } from '@nestjs/common';
|
|
81
|
+
|
|
82
|
+
describe('Cats', () => {
|
|
83
|
+
let app: INestApplication;
|
|
84
|
+
|
|
85
|
+
beforeAll(async () => {
|
|
86
|
+
const moduleRef = await Test.createTestingModule({
|
|
87
|
+
imports: [CatsModule],
|
|
88
|
+
})
|
|
89
|
+
.overrideProvider(CatsService)
|
|
90
|
+
.useValue({ findAll: () => ['test'] })
|
|
91
|
+
.compile();
|
|
92
|
+
|
|
93
|
+
app = moduleRef.createNestApplication();
|
|
94
|
+
await app.init();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('/GET cats', () => {
|
|
98
|
+
return request(app.getHttpServer())
|
|
99
|
+
.get('/cats')
|
|
100
|
+
.expect(200)
|
|
101
|
+
.expect({ data: ['test'] });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
afterAll(async () => {
|
|
105
|
+
await app.close();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Testing Scoped Providers
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const contextId = ContextIdFactory.create();
|
|
114
|
+
jest.spyOn(ContextIdFactory, 'getByRequest').mockImplementation(() => contextId);
|
|
115
|
+
|
|
116
|
+
const service = await moduleRef.resolve(CatsService, contextId);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Override Methods
|
|
120
|
+
|
|
121
|
+
| Method | Purpose |
|
|
122
|
+
|--------|---------|
|
|
123
|
+
| `overrideProvider()` | Override a provider |
|
|
124
|
+
| `overrideModule()` | Override a module |
|
|
125
|
+
| `overrideGuard()` | Override a guard |
|
|
126
|
+
| `overrideInterceptor()` | Override an interceptor |
|
|
127
|
+
| `overrideFilter()` | Override an exception filter |
|
|
128
|
+
| `overridePipe()` | Override a pipe |
|
|
129
|
+
|
|
130
|
+
Each returns an object with `useClass`, `useValue`, or `useFactory`.
|
|
131
|
+
|
|
132
|
+
## Key Points
|
|
133
|
+
|
|
134
|
+
- Keep test files near source files with `.spec.ts` or `.test.ts` suffix
|
|
135
|
+
- E2E tests go in `test/` directory with `.e2e-spec.ts` suffix
|
|
136
|
+
- `compile()` is async and must be awaited
|
|
137
|
+
- Use `resolve()` for scoped providers instead of `get()`
|
|
138
|
+
|
|
139
|
+
<!--
|
|
140
|
+
Source references:
|
|
141
|
+
- https://docs.nestjs.com/fundamentals/testing
|
|
142
|
+
-->
|