@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,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mvc-serve-static
|
|
3
|
+
description: MVC template rendering and serving static files/SPA
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MVC and Serve Static
|
|
7
|
+
|
|
8
|
+
## MVC (Template Rendering)
|
|
9
|
+
|
|
10
|
+
### Express Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install hbs
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { NestFactory } from '@nestjs/core';
|
|
18
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { AppModule } from './app.module';
|
|
21
|
+
|
|
22
|
+
async function bootstrap() {
|
|
23
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
24
|
+
app.useStaticAssets(join(__dirname, '..', 'public'));
|
|
25
|
+
app.setBaseViewsDir(join(__dirname, '..', 'views'));
|
|
26
|
+
app.setViewEngine('hbs');
|
|
27
|
+
await app.listen(3000);
|
|
28
|
+
}
|
|
29
|
+
bootstrap();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Controller with @Render
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { Get, Controller, Render } from '@nestjs/common';
|
|
36
|
+
|
|
37
|
+
@Controller()
|
|
38
|
+
export class AppController {
|
|
39
|
+
@Get()
|
|
40
|
+
@Render('index')
|
|
41
|
+
root() {
|
|
42
|
+
return { message: 'Hello world!' };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Dynamic Template Selection
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Get, Controller, Res } from '@nestjs/common';
|
|
51
|
+
import { Response } from 'express';
|
|
52
|
+
|
|
53
|
+
@Controller()
|
|
54
|
+
export class AppController {
|
|
55
|
+
@Get()
|
|
56
|
+
root(@Res() res: Response) {
|
|
57
|
+
return res.render('dynamic-view', { message: 'Hello!' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Fastify
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install @fastify/static @fastify/view handlebars
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
70
|
+
AppModule,
|
|
71
|
+
new FastifyAdapter(),
|
|
72
|
+
);
|
|
73
|
+
app.useStaticAssets({
|
|
74
|
+
root: join(__dirname, '..', 'public'),
|
|
75
|
+
prefix: '/public/',
|
|
76
|
+
});
|
|
77
|
+
app.setViewEngine({
|
|
78
|
+
engine: { handlebars: require('handlebars') },
|
|
79
|
+
templates: join(__dirname, '..', 'views'),
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
With Fastify, include file extension in `@Render()`: `@Render('index.hbs')`
|
|
84
|
+
|
|
85
|
+
## Serve Static (SPA)
|
|
86
|
+
|
|
87
|
+
Serve static content like Single Page Applications.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install @nestjs/serve-static
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { Module } from '@nestjs/common';
|
|
95
|
+
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
96
|
+
import { join } from 'path';
|
|
97
|
+
|
|
98
|
+
@Module({
|
|
99
|
+
imports: [
|
|
100
|
+
ServeStaticModule.forRoot({
|
|
101
|
+
rootPath: join(__dirname, '..', 'client'),
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
})
|
|
105
|
+
export class AppModule {}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### SPA Client-Side Routing
|
|
109
|
+
|
|
110
|
+
Default `renderPath` is `*` (all paths). The module sends `index.html` for non-matching routes, enabling client-side routing. Controller paths take precedence.
|
|
111
|
+
|
|
112
|
+
### Configuration Options
|
|
113
|
+
|
|
114
|
+
| Option | Description |
|
|
115
|
+
|--------|-------------|
|
|
116
|
+
| `rootPath` | Path to static files |
|
|
117
|
+
| `serveRoot` | URL path prefix |
|
|
118
|
+
| `renderPath` | Paths to serve index.html |
|
|
119
|
+
| `exclude` | Excluded paths |
|
|
120
|
+
|
|
121
|
+
## Key Points
|
|
122
|
+
|
|
123
|
+
- Use `NestExpressApplication` or `NestFastifyApplication` for MVC
|
|
124
|
+
- `@Render()` passes return object to template as variables
|
|
125
|
+
- ServeStatic fallback enables SPA routing
|
|
126
|
+
- Fastify: set `serveStaticOptions.fallthrough: true` for SPA fallback
|
|
127
|
+
|
|
128
|
+
<!--
|
|
129
|
+
Source references:
|
|
130
|
+
- https://docs.nestjs.com/techniques/mvc
|
|
131
|
+
- https://docs.nestjs.com/recipes/serve-static
|
|
132
|
+
-->
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: queues
|
|
3
|
+
description: Background job processing with BullMQ and Bull
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Queues
|
|
7
|
+
|
|
8
|
+
Queues help with task scheduling, load balancing, and background processing using Redis-backed job queues.
|
|
9
|
+
|
|
10
|
+
## Installation (BullMQ - Recommended)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/bullmq bullmq
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Module } from '@nestjs/common';
|
|
20
|
+
import { BullModule } from '@nestjs/bullmq';
|
|
21
|
+
|
|
22
|
+
@Module({
|
|
23
|
+
imports: [
|
|
24
|
+
BullModule.forRoot({
|
|
25
|
+
connection: {
|
|
26
|
+
host: 'localhost',
|
|
27
|
+
port: 6379,
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
BullModule.registerQueue({ name: 'audio' }),
|
|
31
|
+
],
|
|
32
|
+
})
|
|
33
|
+
export class AppModule {}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Producer (Adding Jobs)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Injectable } from '@nestjs/common';
|
|
40
|
+
import { InjectQueue } from '@nestjs/bullmq';
|
|
41
|
+
import { Queue } from 'bullmq';
|
|
42
|
+
|
|
43
|
+
@Injectable()
|
|
44
|
+
export class AudioService {
|
|
45
|
+
constructor(@InjectQueue('audio') private audioQueue: Queue) {}
|
|
46
|
+
|
|
47
|
+
async addTranscodeJob(file: string) {
|
|
48
|
+
await this.audioQueue.add('transcode', { file });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// With options
|
|
52
|
+
async addDelayedJob(data: any) {
|
|
53
|
+
await this.audioQueue.add('process', data, {
|
|
54
|
+
delay: 5000, // 5 second delay
|
|
55
|
+
attempts: 3, // Retry 3 times
|
|
56
|
+
removeOnComplete: true,
|
|
57
|
+
priority: 1, // Higher priority
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Consumer (Processing Jobs)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { Processor, WorkerHost, OnWorkerEvent } from '@nestjs/bullmq';
|
|
67
|
+
import { Job } from 'bullmq';
|
|
68
|
+
|
|
69
|
+
@Processor('audio')
|
|
70
|
+
export class AudioConsumer extends WorkerHost {
|
|
71
|
+
async process(job: Job<any, any, string>): Promise<any> {
|
|
72
|
+
switch (job.name) {
|
|
73
|
+
case 'transcode':
|
|
74
|
+
return this.transcode(job.data);
|
|
75
|
+
case 'compress':
|
|
76
|
+
return this.compress(job.data);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async transcode(data: any) {
|
|
81
|
+
// Processing logic
|
|
82
|
+
await job.updateProgress(50);
|
|
83
|
+
return { processed: true };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@OnWorkerEvent('completed')
|
|
87
|
+
onCompleted(job: Job) {
|
|
88
|
+
console.log(`Job ${job.id} completed`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@OnWorkerEvent('failed')
|
|
92
|
+
onFailed(job: Job, error: Error) {
|
|
93
|
+
console.error(`Job ${job.id} failed:`, error.message);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Register consumer as provider:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
@Module({
|
|
102
|
+
providers: [AudioConsumer],
|
|
103
|
+
})
|
|
104
|
+
export class AudioModule {}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Queue Events Listener
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { QueueEventsHost, QueueEventsListener, OnQueueEvent } from '@nestjs/bullmq';
|
|
111
|
+
|
|
112
|
+
@QueueEventsListener('audio')
|
|
113
|
+
export class AudioEventsListener extends QueueEventsHost {
|
|
114
|
+
@OnQueueEvent('active')
|
|
115
|
+
onActive(job: { jobId: string }) {
|
|
116
|
+
console.log(`Processing job ${job.jobId}...`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Job Options
|
|
122
|
+
|
|
123
|
+
| Option | Description |
|
|
124
|
+
|--------|-------------|
|
|
125
|
+
| `delay` | Milliseconds to wait before processing |
|
|
126
|
+
| `attempts` | Number of retry attempts |
|
|
127
|
+
| `backoff` | Retry delay strategy |
|
|
128
|
+
| `priority` | Job priority (1 = highest) |
|
|
129
|
+
| `lifo` | Process as LIFO instead of FIFO |
|
|
130
|
+
| `removeOnComplete` | Remove job data after completion |
|
|
131
|
+
| `removeOnFail` | Remove job data after failure |
|
|
132
|
+
| `repeat` | Cron-like repeat configuration |
|
|
133
|
+
|
|
134
|
+
## Queue Management
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
await this.audioQueue.pause();
|
|
138
|
+
await this.audioQueue.resume();
|
|
139
|
+
await this.audioQueue.clean(1000); // Remove completed jobs older than 1s
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Request-Scoped Consumers
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
@Processor({ name: 'audio', scope: Scope.REQUEST })
|
|
146
|
+
export class AudioConsumer extends WorkerHost {
|
|
147
|
+
constructor(@Inject(JOB_REF) private jobRef: Job) {}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Key Points
|
|
152
|
+
|
|
153
|
+
- Consumers must be registered as providers
|
|
154
|
+
- BullMQ is recommended over Bull (actively developed)
|
|
155
|
+
- Use `switch` statement in `process()` for different job names
|
|
156
|
+
- Requires Redis for job persistence
|
|
157
|
+
- Jobs persist across restarts
|
|
158
|
+
|
|
159
|
+
<!--
|
|
160
|
+
Source references:
|
|
161
|
+
- https://docs.nestjs.com/techniques/queues
|
|
162
|
+
-->
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: serialization
|
|
3
|
+
description: Response serialization with class-transformer
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Serialization
|
|
7
|
+
|
|
8
|
+
Serialization transforms objects before sending responses, using `class-transformer` decorators.
|
|
9
|
+
|
|
10
|
+
## Basic Usage
|
|
11
|
+
|
|
12
|
+
Use `ClassSerializerInterceptor` to automatically transform responses:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { Controller, Get, UseInterceptors } from '@nestjs/common';
|
|
16
|
+
import { ClassSerializerInterceptor } from '@nestjs/common';
|
|
17
|
+
|
|
18
|
+
@Controller('users')
|
|
19
|
+
@UseInterceptors(ClassSerializerInterceptor)
|
|
20
|
+
export class UsersController {
|
|
21
|
+
@Get(':id')
|
|
22
|
+
findOne() {
|
|
23
|
+
return new UserEntity({
|
|
24
|
+
id: 1,
|
|
25
|
+
firstName: 'John',
|
|
26
|
+
lastName: 'Doe',
|
|
27
|
+
password: 'secret123',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Exclude Properties
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Exclude } from 'class-transformer';
|
|
37
|
+
|
|
38
|
+
export class UserEntity {
|
|
39
|
+
id: number;
|
|
40
|
+
firstName: string;
|
|
41
|
+
lastName: string;
|
|
42
|
+
|
|
43
|
+
@Exclude()
|
|
44
|
+
password: string;
|
|
45
|
+
|
|
46
|
+
constructor(partial: Partial<UserEntity>) {
|
|
47
|
+
Object.assign(this, partial);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Response excludes `password`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{ "id": 1, "firstName": "John", "lastName": "Doe" }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Expose Computed Properties
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Expose } from 'class-transformer';
|
|
62
|
+
|
|
63
|
+
export class UserEntity {
|
|
64
|
+
firstName: string;
|
|
65
|
+
lastName: string;
|
|
66
|
+
|
|
67
|
+
@Expose()
|
|
68
|
+
get fullName(): string {
|
|
69
|
+
return `${this.firstName} ${this.lastName}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Transform Properties
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Transform } from 'class-transformer';
|
|
78
|
+
|
|
79
|
+
export class UserEntity {
|
|
80
|
+
@Transform(({ value }) => value.name)
|
|
81
|
+
role: RoleEntity; // Outputs role.name instead of full object
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Serialization Options
|
|
86
|
+
|
|
87
|
+
Per-route options:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { SerializeOptions } from '@nestjs/common';
|
|
91
|
+
|
|
92
|
+
@Get()
|
|
93
|
+
@SerializeOptions({ excludePrefixes: ['_'] })
|
|
94
|
+
findAll() {
|
|
95
|
+
return this.usersService.findAll();
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Transform Plain Objects
|
|
100
|
+
|
|
101
|
+
Force transformation of plain objects:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
@Get()
|
|
105
|
+
@UseInterceptors(ClassSerializerInterceptor)
|
|
106
|
+
@SerializeOptions({ type: UserEntity })
|
|
107
|
+
findOne() {
|
|
108
|
+
return {
|
|
109
|
+
id: 1,
|
|
110
|
+
firstName: 'John',
|
|
111
|
+
password: 'secret', // Will be excluded
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Global Interceptor
|
|
117
|
+
|
|
118
|
+
Apply globally:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
@Module({
|
|
122
|
+
providers: [
|
|
123
|
+
{ provide: APP_INTERCEPTOR, useClass: ClassSerializerInterceptor },
|
|
124
|
+
],
|
|
125
|
+
})
|
|
126
|
+
export class AppModule {}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Groups
|
|
130
|
+
|
|
131
|
+
Expose different fields based on groups:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
export class UserEntity {
|
|
135
|
+
@Expose({ groups: ['admin'] })
|
|
136
|
+
email: string;
|
|
137
|
+
|
|
138
|
+
@Expose({ groups: ['admin', 'user'] })
|
|
139
|
+
firstName: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Controller
|
|
143
|
+
@SerializeOptions({ groups: ['admin'] })
|
|
144
|
+
@Get('admin')
|
|
145
|
+
findForAdmin() {}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Key Points
|
|
149
|
+
|
|
150
|
+
- Must return class instances, not plain objects
|
|
151
|
+
- Works with WebSockets and Microservices
|
|
152
|
+
- `@Exclude()` and `@Expose()` from `class-transformer`
|
|
153
|
+
- Use `excludeExtraneousValues: true` to only include `@Expose()` fields
|
|
154
|
+
|
|
155
|
+
<!--
|
|
156
|
+
Source references:
|
|
157
|
+
- https://docs.nestjs.com/techniques/serialization
|
|
158
|
+
-->
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sessions-cookies
|
|
3
|
+
description: HTTP sessions and cookies for stateful applications
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sessions and Cookies
|
|
7
|
+
|
|
8
|
+
## Sessions
|
|
9
|
+
|
|
10
|
+
### Express (Default)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install express-session
|
|
14
|
+
npm install -D @types/express-session
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import * as session from 'express-session';
|
|
19
|
+
|
|
20
|
+
// main.ts
|
|
21
|
+
app.use(
|
|
22
|
+
session({
|
|
23
|
+
secret: 'my-secret',
|
|
24
|
+
resave: false,
|
|
25
|
+
saveUninitialized: false,
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Usage in controllers:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
@Get()
|
|
34
|
+
findAll(@Req() request: Request) {
|
|
35
|
+
request.session.visits = (request.session.visits ?? 0) + 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Or using @Session decorator
|
|
39
|
+
@Get()
|
|
40
|
+
findAll(@Session() session: Record<string, any>) {
|
|
41
|
+
session.visits = (session.visits ?? 0) + 1;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Fastify
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @fastify/secure-session
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import secureSession from '@fastify/secure-session';
|
|
53
|
+
|
|
54
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
55
|
+
AppModule,
|
|
56
|
+
new FastifyAdapter(),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
await app.register(secureSession, {
|
|
60
|
+
secret: 'averylogphrasebiggerthanthirtytwochars',
|
|
61
|
+
salt: 'mq9hDxBVDbspDR6n',
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
@Get()
|
|
67
|
+
findAll(@Req() request: FastifyRequest) {
|
|
68
|
+
const visits = request.session.get('visits') ?? 0;
|
|
69
|
+
request.session.set('visits', visits + 1);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Cookies
|
|
74
|
+
|
|
75
|
+
### Express (Default)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install cookie-parser
|
|
79
|
+
npm install -D @types/cookie-parser
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import * as cookieParser from 'cookie-parser';
|
|
84
|
+
|
|
85
|
+
app.use(cookieParser());
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Reading and setting cookies:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
@Get()
|
|
92
|
+
getCookies(@Req() request: Request) {
|
|
93
|
+
console.log(request.cookies);
|
|
94
|
+
console.log(request.signedCookies);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Get()
|
|
98
|
+
setCookie(@Res({ passthrough: true }) response: Response) {
|
|
99
|
+
response.cookie('key', 'value', {
|
|
100
|
+
httpOnly: true,
|
|
101
|
+
secure: true,
|
|
102
|
+
maxAge: 1000 * 60 * 60 * 24, // 1 day
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Fastify
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install @fastify/cookie
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import fastifyCookie from '@fastify/cookie';
|
|
115
|
+
|
|
116
|
+
await app.register(fastifyCookie, {
|
|
117
|
+
secret: 'my-secret',
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
@Get()
|
|
123
|
+
setCookie(@Res({ passthrough: true }) response: FastifyReply) {
|
|
124
|
+
response.setCookie('key', 'value');
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Custom Cookie Decorator
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
132
|
+
|
|
133
|
+
export const Cookies = createParamDecorator(
|
|
134
|
+
(data: string, ctx: ExecutionContext) => {
|
|
135
|
+
const request = ctx.switchToHttp().getRequest();
|
|
136
|
+
return data ? request.cookies?.[data] : request.cookies;
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Usage
|
|
141
|
+
@Get()
|
|
142
|
+
findAll(@Cookies('name') name: string) {}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Session Configuration Options
|
|
146
|
+
|
|
147
|
+
| Option | Description |
|
|
148
|
+
|--------|-------------|
|
|
149
|
+
| `secret` | Used to sign session ID cookie |
|
|
150
|
+
| `resave` | Force save even if unmodified (set to `false`) |
|
|
151
|
+
| `saveUninitialized` | Save uninitialized sessions (set to `false` for login flows) |
|
|
152
|
+
| `cookie.secure` | Require HTTPS (recommended for production) |
|
|
153
|
+
| `cookie.httpOnly` | Prevent client-side access |
|
|
154
|
+
|
|
155
|
+
## Key Points
|
|
156
|
+
|
|
157
|
+
- Default session storage is not for production (leaks memory)
|
|
158
|
+
- Use Redis or database-backed session store in production
|
|
159
|
+
- Set `secure: true` for production cookies
|
|
160
|
+
- Use `passthrough: true` with `@Res()` to keep NestJS response handling
|
|
161
|
+
- Session decorator is `@Session()` from `@nestjs/common`
|
|
162
|
+
|
|
163
|
+
<!--
|
|
164
|
+
Source references:
|
|
165
|
+
- https://docs.nestjs.com/techniques/session
|
|
166
|
+
- https://docs.nestjs.com/techniques/cookies
|
|
167
|
+
-->
|