@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,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: events
|
|
3
|
+
description: Event-driven architecture with @nestjs/event-emitter
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Events
|
|
7
|
+
|
|
8
|
+
The `@nestjs/event-emitter` package provides event-driven architecture for decoupled application design.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/event-emitter
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Module } from '@nestjs/common';
|
|
20
|
+
import { EventEmitterModule } from '@nestjs/event-emitter';
|
|
21
|
+
|
|
22
|
+
@Module({
|
|
23
|
+
imports: [EventEmitterModule.forRoot()],
|
|
24
|
+
})
|
|
25
|
+
export class AppModule {}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Dispatching Events
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { Injectable } from '@nestjs/common';
|
|
32
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
33
|
+
|
|
34
|
+
@Injectable()
|
|
35
|
+
export class OrdersService {
|
|
36
|
+
constructor(private eventEmitter: EventEmitter2) {}
|
|
37
|
+
|
|
38
|
+
createOrder(data: CreateOrderDto) {
|
|
39
|
+
const order = this.ordersRepository.create(data);
|
|
40
|
+
|
|
41
|
+
this.eventEmitter.emit('order.created', new OrderCreatedEvent({
|
|
42
|
+
orderId: order.id,
|
|
43
|
+
userId: data.userId,
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
return order;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Listening to Events
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Injectable } from '@nestjs/common';
|
|
55
|
+
import { OnEvent } from '@nestjs/event-emitter';
|
|
56
|
+
|
|
57
|
+
@Injectable()
|
|
58
|
+
export class NotificationsListener {
|
|
59
|
+
@OnEvent('order.created')
|
|
60
|
+
handleOrderCreated(event: OrderCreatedEvent) {
|
|
61
|
+
// Send notification to user
|
|
62
|
+
console.log(`Order ${event.orderId} created`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@OnEvent('order.created', { async: true })
|
|
66
|
+
async sendEmailNotification(event: OrderCreatedEvent) {
|
|
67
|
+
await this.emailService.send(event);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Wildcard Listeners
|
|
73
|
+
|
|
74
|
+
Enable wildcards in configuration:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
EventEmitterModule.forRoot({
|
|
78
|
+
wildcard: true,
|
|
79
|
+
delimiter: '.',
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use wildcards:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
@OnEvent('order.*')
|
|
87
|
+
handleAllOrderEvents(event: any) {
|
|
88
|
+
// Handles order.created, order.updated, order.shipped, etc.
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@OnEvent('**')
|
|
92
|
+
handleAllEvents(event: any) {
|
|
93
|
+
// Handles all events
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Configuration Options
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
EventEmitterModule.forRoot({
|
|
101
|
+
wildcard: false, // Enable wildcard patterns
|
|
102
|
+
delimiter: '.', // Namespace delimiter
|
|
103
|
+
newListener: false, // Emit newListener event
|
|
104
|
+
removeListener: false, // Emit removeListener event
|
|
105
|
+
maxListeners: 10, // Max listeners per event
|
|
106
|
+
verboseMemoryLeak: false, // Show event name in leak warning
|
|
107
|
+
ignoreErrors: false, // Don't throw on unhandled errors
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Listener Options
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
@OnEvent('order.created', {
|
|
115
|
+
async: true, // Handle asynchronously
|
|
116
|
+
prependListener: true, // Add to beginning of listener array
|
|
117
|
+
suppressErrors: true, // Don't throw errors
|
|
118
|
+
})
|
|
119
|
+
handleOrderCreated(event: OrderCreatedEvent) {}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Event Class Pattern
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
export class OrderCreatedEvent {
|
|
126
|
+
constructor(
|
|
127
|
+
public readonly orderId: string,
|
|
128
|
+
public readonly userId: string,
|
|
129
|
+
public readonly createdAt: Date = new Date(),
|
|
130
|
+
) {}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Preventing Event Loss
|
|
135
|
+
|
|
136
|
+
Events emitted before `onApplicationBootstrap` may be missed:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
@Injectable()
|
|
140
|
+
export class MyService implements OnApplicationBootstrap {
|
|
141
|
+
constructor(
|
|
142
|
+
private eventEmitter: EventEmitter2,
|
|
143
|
+
private eventEmitterReadinessWatcher: EventEmitterReadinessWatcher,
|
|
144
|
+
) {}
|
|
145
|
+
|
|
146
|
+
async onApplicationBootstrap() {
|
|
147
|
+
await this.eventEmitterReadinessWatcher.waitUntilReady();
|
|
148
|
+
this.eventEmitter.emit('app.ready', {});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Key Points
|
|
154
|
+
|
|
155
|
+
- Event subscribers cannot be request-scoped
|
|
156
|
+
- Multiple listeners can subscribe to the same event
|
|
157
|
+
- Events are great for decoupling modules
|
|
158
|
+
- Use async: true for non-blocking event handling
|
|
159
|
+
|
|
160
|
+
<!--
|
|
161
|
+
Source references:
|
|
162
|
+
- https://docs.nestjs.com/techniques/events
|
|
163
|
+
-->
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fastify
|
|
3
|
+
description: Using Fastify as the HTTP adapter for better performance
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fastify (Performance)
|
|
7
|
+
|
|
8
|
+
Fastify is a high-performance alternative to Express, achieving nearly 2x better benchmark results.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/platform-fastify
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { NestFactory } from '@nestjs/core';
|
|
20
|
+
import {
|
|
21
|
+
FastifyAdapter,
|
|
22
|
+
NestFastifyApplication,
|
|
23
|
+
} from '@nestjs/platform-fastify';
|
|
24
|
+
import { AppModule } from './app.module';
|
|
25
|
+
|
|
26
|
+
async function bootstrap() {
|
|
27
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
28
|
+
AppModule,
|
|
29
|
+
new FastifyAdapter(),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Fastify listens only on localhost by default
|
|
33
|
+
await app.listen(3000, '0.0.0.0');
|
|
34
|
+
}
|
|
35
|
+
bootstrap();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Fastify Options
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
new FastifyAdapter({
|
|
42
|
+
logger: true,
|
|
43
|
+
trustProxy: true,
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Middleware Differences
|
|
48
|
+
|
|
49
|
+
Fastify middleware receives raw `req` and `res` objects:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Injectable, NestMiddleware } from '@nestjs/common';
|
|
53
|
+
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
54
|
+
|
|
55
|
+
@Injectable()
|
|
56
|
+
export class LoggerMiddleware implements NestMiddleware {
|
|
57
|
+
use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) {
|
|
58
|
+
console.log('Request...');
|
|
59
|
+
next();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Redirect Response
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
@Get()
|
|
68
|
+
redirect(@Res() res: FastifyReply) {
|
|
69
|
+
res.status(302).redirect('/login');
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Route Config
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { RouteConfig } from '@nestjs/platform-fastify';
|
|
77
|
+
|
|
78
|
+
@RouteConfig({ output: 'hello world' })
|
|
79
|
+
@Get()
|
|
80
|
+
index(@Req() req: FastifyRequest) {
|
|
81
|
+
return req.routeConfig.output;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Route Constraints
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { RouteConstraints } from '@nestjs/platform-fastify';
|
|
89
|
+
|
|
90
|
+
@RouteConstraints({ version: '1.2.x' })
|
|
91
|
+
@Get()
|
|
92
|
+
newFeature() {
|
|
93
|
+
return 'Works only for version >= 1.2.x';
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Registering Plugins
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import compression from '@fastify/compress';
|
|
101
|
+
import helmet from '@fastify/helmet';
|
|
102
|
+
import fastifyCookie from '@fastify/cookie';
|
|
103
|
+
|
|
104
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
105
|
+
AppModule,
|
|
106
|
+
new FastifyAdapter(),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await app.register(compression);
|
|
110
|
+
await app.register(helmet);
|
|
111
|
+
await app.register(fastifyCookie, { secret: 'my-secret' });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Platform-Specific Packages
|
|
115
|
+
|
|
116
|
+
When using Fastify, replace Express packages with Fastify equivalents:
|
|
117
|
+
|
|
118
|
+
| Express | Fastify |
|
|
119
|
+
|---------|---------|
|
|
120
|
+
| `express-session` | `@fastify/secure-session` |
|
|
121
|
+
| `cookie-parser` | `@fastify/cookie` |
|
|
122
|
+
| `compression` | `@fastify/compress` |
|
|
123
|
+
| `helmet` | `@fastify/helmet` |
|
|
124
|
+
| `multer` | Not compatible |
|
|
125
|
+
|
|
126
|
+
## Key Points
|
|
127
|
+
|
|
128
|
+
- Provides significant performance improvements
|
|
129
|
+
- Not all Express middleware is compatible
|
|
130
|
+
- Use `NestFastifyApplication` type for proper typings
|
|
131
|
+
- Default listener is `localhost` only (specify `0.0.0.0` for external access)
|
|
132
|
+
- File upload (multer) is not compatible with Fastify
|
|
133
|
+
|
|
134
|
+
<!--
|
|
135
|
+
Source references:
|
|
136
|
+
- https://docs.nestjs.com/techniques/performance
|
|
137
|
+
-->
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: file-upload
|
|
3
|
+
description: File upload handling with multer integration
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# File Upload
|
|
7
|
+
|
|
8
|
+
NestJS uses [multer](https://github.com/expressjs/multer) middleware for handling `multipart/form-data` file uploads.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i -D @types/multer
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Single File Upload
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
|
|
20
|
+
import { FileInterceptor } from '@nestjs/platform-express';
|
|
21
|
+
import { Express } from 'express';
|
|
22
|
+
|
|
23
|
+
@Controller('upload')
|
|
24
|
+
export class UploadController {
|
|
25
|
+
@Post()
|
|
26
|
+
@UseInterceptors(FileInterceptor('file'))
|
|
27
|
+
uploadFile(@UploadedFile() file: Express.Multer.File) {
|
|
28
|
+
console.log(file);
|
|
29
|
+
return { filename: file.originalname };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## File Validation
|
|
35
|
+
|
|
36
|
+
Use `ParseFilePipe` with built-in validators:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
@Post()
|
|
40
|
+
uploadFile(
|
|
41
|
+
@UploadedFile(
|
|
42
|
+
new ParseFilePipe({
|
|
43
|
+
validators: [
|
|
44
|
+
new MaxFileSizeValidator({ maxSize: 1000000 }), // 1MB
|
|
45
|
+
new FileTypeValidator({ fileType: 'image/jpeg' }),
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
49
|
+
file: Express.Multer.File,
|
|
50
|
+
) {
|
|
51
|
+
return { filename: file.originalname };
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Using builder pattern:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
@UploadedFile(
|
|
59
|
+
new ParseFilePipeBuilder()
|
|
60
|
+
.addFileTypeValidator({ fileType: 'jpeg' })
|
|
61
|
+
.addMaxSizeValidator({ maxSize: 1000000 })
|
|
62
|
+
.build({ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }),
|
|
63
|
+
)
|
|
64
|
+
file: Express.Multer.File,
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Multiple Files (Same Field)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
@Post('uploads')
|
|
71
|
+
@UseInterceptors(FilesInterceptor('files', 10)) // max 10 files
|
|
72
|
+
uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
|
|
73
|
+
return files.map(f => f.originalname);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Multiple Files (Different Fields)
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
@Post('uploads')
|
|
81
|
+
@UseInterceptors(FileFieldsInterceptor([
|
|
82
|
+
{ name: 'avatar', maxCount: 1 },
|
|
83
|
+
{ name: 'background', maxCount: 1 },
|
|
84
|
+
]))
|
|
85
|
+
uploadFiles(
|
|
86
|
+
@UploadedFiles() files: {
|
|
87
|
+
avatar?: Express.Multer.File[],
|
|
88
|
+
background?: Express.Multer.File[]
|
|
89
|
+
},
|
|
90
|
+
) {
|
|
91
|
+
return { avatar: files.avatar?.[0], background: files.background?.[0] };
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Any Files
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
@Post('any')
|
|
99
|
+
@UseInterceptors(AnyFilesInterceptor())
|
|
100
|
+
uploadAny(@UploadedFiles() files: Array<Express.Multer.File>) {
|
|
101
|
+
return files;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Configure Destination
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { MulterModule } from '@nestjs/platform-express';
|
|
109
|
+
|
|
110
|
+
@Module({
|
|
111
|
+
imports: [
|
|
112
|
+
MulterModule.register({ dest: './uploads' }),
|
|
113
|
+
],
|
|
114
|
+
})
|
|
115
|
+
export class UploadModule {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Async configuration:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
MulterModule.registerAsync({
|
|
122
|
+
imports: [ConfigModule],
|
|
123
|
+
useFactory: async (configService: ConfigService) => ({
|
|
124
|
+
dest: configService.get('UPLOAD_DEST'),
|
|
125
|
+
}),
|
|
126
|
+
inject: [ConfigService],
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Key Points
|
|
131
|
+
|
|
132
|
+
- `FileInterceptor` field name must match form field name
|
|
133
|
+
- Not compatible with Fastify adapter
|
|
134
|
+
- Use `fileIsRequired: false` for optional files
|
|
135
|
+
- Multer only processes `multipart/form-data`
|
|
136
|
+
|
|
137
|
+
<!--
|
|
138
|
+
Source references:
|
|
139
|
+
- https://docs.nestjs.com/techniques/file-upload
|
|
140
|
+
-->
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: http-module
|
|
3
|
+
description: Making HTTP requests with @nestjs/axios
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HTTP Module
|
|
7
|
+
|
|
8
|
+
NestJS wraps Axios for making HTTP requests to external services.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/axios axios
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Module } from '@nestjs/common';
|
|
20
|
+
import { HttpModule } from '@nestjs/axios';
|
|
21
|
+
|
|
22
|
+
@Module({
|
|
23
|
+
imports: [HttpModule],
|
|
24
|
+
providers: [CatsService],
|
|
25
|
+
})
|
|
26
|
+
export class CatsModule {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Basic Usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Injectable } from '@nestjs/common';
|
|
33
|
+
import { HttpService } from '@nestjs/axios';
|
|
34
|
+
import { firstValueFrom } from 'rxjs';
|
|
35
|
+
import { AxiosResponse } from 'axios';
|
|
36
|
+
|
|
37
|
+
@Injectable()
|
|
38
|
+
export class CatsService {
|
|
39
|
+
constructor(private readonly httpService: HttpService) {}
|
|
40
|
+
|
|
41
|
+
// Using Observable
|
|
42
|
+
findAll(): Observable<AxiosResponse<Cat[]>> {
|
|
43
|
+
return this.httpService.get('https://api.example.com/cats');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Using Promise
|
|
47
|
+
async findAllAsync(): Promise<Cat[]> {
|
|
48
|
+
const { data } = await firstValueFrom(
|
|
49
|
+
this.httpService.get<Cat[]>('https://api.example.com/cats'),
|
|
50
|
+
);
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## HTTP Methods
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// GET
|
|
60
|
+
this.httpService.get('/users');
|
|
61
|
+
|
|
62
|
+
// POST
|
|
63
|
+
this.httpService.post('/users', { name: 'John' });
|
|
64
|
+
|
|
65
|
+
// PUT
|
|
66
|
+
this.httpService.put('/users/1', { name: 'Jane' });
|
|
67
|
+
|
|
68
|
+
// PATCH
|
|
69
|
+
this.httpService.patch('/users/1', { name: 'Jane' });
|
|
70
|
+
|
|
71
|
+
// DELETE
|
|
72
|
+
this.httpService.delete('/users/1');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
HttpModule.register({
|
|
79
|
+
timeout: 5000,
|
|
80
|
+
maxRedirects: 5,
|
|
81
|
+
baseURL: 'https://api.example.com',
|
|
82
|
+
headers: {
|
|
83
|
+
'Authorization': 'Bearer token',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Async Configuration
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
HttpModule.registerAsync({
|
|
92
|
+
imports: [ConfigModule],
|
|
93
|
+
useFactory: async (configService: ConfigService) => ({
|
|
94
|
+
timeout: configService.get('HTTP_TIMEOUT'),
|
|
95
|
+
maxRedirects: configService.get('HTTP_MAX_REDIRECTS'),
|
|
96
|
+
baseURL: configService.get('API_URL'),
|
|
97
|
+
}),
|
|
98
|
+
inject: [ConfigService],
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Error Handling
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { catchError, firstValueFrom } from 'rxjs';
|
|
106
|
+
import { AxiosError } from 'axios';
|
|
107
|
+
|
|
108
|
+
async findAll(): Promise<Cat[]> {
|
|
109
|
+
const { data } = await firstValueFrom(
|
|
110
|
+
this.httpService.get<Cat[]>('/cats').pipe(
|
|
111
|
+
catchError((error: AxiosError) => {
|
|
112
|
+
this.logger.error(error.response?.data);
|
|
113
|
+
throw new HttpException(
|
|
114
|
+
'External API error',
|
|
115
|
+
HttpStatus.BAD_GATEWAY,
|
|
116
|
+
);
|
|
117
|
+
}),
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
return data;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Direct Axios Access
|
|
125
|
+
|
|
126
|
+
Access the underlying Axios instance:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
@Injectable()
|
|
130
|
+
export class CatsService {
|
|
131
|
+
constructor(private readonly httpService: HttpService) {}
|
|
132
|
+
|
|
133
|
+
async findAll(): Promise<Cat[]> {
|
|
134
|
+
const { data } = await this.httpService.axiosRef.get('/cats');
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Interceptors
|
|
141
|
+
|
|
142
|
+
Add Axios interceptors:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
@Injectable()
|
|
146
|
+
export class CatsService implements OnModuleInit {
|
|
147
|
+
constructor(private readonly httpService: HttpService) {}
|
|
148
|
+
|
|
149
|
+
onModuleInit() {
|
|
150
|
+
this.httpService.axiosRef.interceptors.request.use((config) => {
|
|
151
|
+
config.headers['X-Request-Id'] = uuid();
|
|
152
|
+
return config;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this.httpService.axiosRef.interceptors.response.use(
|
|
156
|
+
(response) => response,
|
|
157
|
+
(error) => {
|
|
158
|
+
// Log error
|
|
159
|
+
return Promise.reject(error);
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Key Points
|
|
167
|
+
|
|
168
|
+
- Returns RxJS Observables by default
|
|
169
|
+
- Use `firstValueFrom` or `lastValueFrom` for Promises
|
|
170
|
+
- `AxiosResponse` is from `axios` package
|
|
171
|
+
- Configuration passed directly to Axios constructor
|
|
172
|
+
|
|
173
|
+
<!--
|
|
174
|
+
Source references:
|
|
175
|
+
- https://docs.nestjs.com/techniques/http-module
|
|
176
|
+
-->
|