claude-code-arcane 1.0.1 → 1.1.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/CHANGELOG.md +7 -0
- package/agents/engineering/e2e-tester.md +65 -0
- package/agents/engineering/nestjs-engineer.md +74 -0
- package/agents/engineering/nextjs-engineer.md +72 -0
- package/agents/engineering/nextjs-reviewer.md +40 -0
- package/package.json +1 -1
- package/profiles/backend-nestjs.yaml +58 -0
- package/profiles/backend-nextjs.yaml +56 -0
- package/rules/nestjs-code.md +73 -0
- package/rules/nextjs-code.md +60 -0
- package/rules/react-perf.md +59 -0
- package/skills/ai-sdk-setup/SKILL.md +82 -0
- package/skills/nestjs-best-practices/SKILL.md +70 -0
- package/skills/nestjs-best-practices/references/api-design.md +34 -0
- package/skills/nestjs-best-practices/references/architecture.md +53 -0
- package/skills/nestjs-best-practices/references/database.md +37 -0
- package/skills/nestjs-best-practices/references/dependency-injection.md +45 -0
- package/skills/nestjs-best-practices/references/error-handling.md +43 -0
- package/skills/nestjs-best-practices/references/microservices-devops.md +41 -0
- package/skills/nestjs-best-practices/references/performance.md +32 -0
- package/skills/nestjs-best-practices/references/security.md +50 -0
- package/skills/nestjs-best-practices/references/testing.md +32 -0
- package/skills/nestjs-scaffold/SKILL.md +75 -0
- package/skills/nextjs-best-practices/SKILL.md +64 -0
- package/skills/nextjs-best-practices/references/advanced.md +21 -0
- package/skills/nextjs-best-practices/references/bundle-size.md +32 -0
- package/skills/nextjs-best-practices/references/client-fetching.md +20 -0
- package/skills/nextjs-best-practices/references/javascript.md +47 -0
- package/skills/nextjs-best-practices/references/rendering.md +46 -0
- package/skills/nextjs-best-practices/references/rerender.md +55 -0
- package/skills/nextjs-best-practices/references/server.md +47 -0
- package/skills/nextjs-best-practices/references/waterfalls.md +41 -0
- package/skills/nextjs-scaffold/SKILL.md +76 -0
- package/skills/pgvector-search/SKILL.md +67 -0
- package/skills/seo-nextjs/SKILL.md +81 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-best-practices
|
|
3
|
+
description: "NestJS production best practices: 40 reglas en 10 categorías (arquitectura, DI, errores, seguridad, performance, testing, DB/ORM, API, microservicios, devops) priorizadas por impacto. Usar al escribir/revisar código NestJS."
|
|
4
|
+
category: "backend"
|
|
5
|
+
argument-hint: "[architecture|di|security|performance|testing|database|api|all]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nestjs-best-practices — Guía priorizada por impacto
|
|
10
|
+
|
|
11
|
+
40 reglas para aplicaciones NestJS production-ready, organizadas en 10 categorías y priorizadas de **CRITICAL** a **LOW-MEDIUM**. Cada regla del catálogo (en `references/`) trae impacto, ejemplo incorrecto, ejemplo correcto y link a docs.
|
|
12
|
+
|
|
13
|
+
## MANDATORY WORKFLOW
|
|
14
|
+
|
|
15
|
+
**No aplicar las 40 reglas a ciegas. Diagnosticar primero, priorizar por impacto.**
|
|
16
|
+
|
|
17
|
+
### Step 0: Determinar scope
|
|
18
|
+
|
|
19
|
+
1. **¿Código nuevo o auditoría de existente?** Si es auditoría, leer primero el módulo/servicio objetivo.
|
|
20
|
+
2. **¿Qué categoría aplica?** Mapear el área del problema (ver tabla) o usar `all` para review completa.
|
|
21
|
+
3. **¿Hay síntomas concretos?** (crashes, lentitud, N+1, leaks) → ir directo a la categoría relevante.
|
|
22
|
+
|
|
23
|
+
### Step 1: Priorizar por impacto
|
|
24
|
+
|
|
25
|
+
Aplicar en este orden — no bajar de nivel hasta resolver el anterior:
|
|
26
|
+
|
|
27
|
+
| Prioridad | Categoría | Impacto | Reference |
|
|
28
|
+
|-----------|-----------|---------|-----------|
|
|
29
|
+
| 1 | Architecture | CRITICAL | `references/architecture.md` |
|
|
30
|
+
| 2 | Dependency Injection | CRITICAL | `references/dependency-injection.md` |
|
|
31
|
+
| 3 | Error Handling | HIGH | `references/error-handling.md` |
|
|
32
|
+
| 4 | Security | HIGH | `references/security.md` |
|
|
33
|
+
| 5 | Performance | HIGH | `references/performance.md` |
|
|
34
|
+
| 6 | Database & ORM | MEDIUM-HIGH | `references/database.md` |
|
|
35
|
+
| 7 | Testing | MEDIUM-HIGH | `references/testing.md` |
|
|
36
|
+
| 8 | API Design | MEDIUM | `references/api-design.md` |
|
|
37
|
+
| 9 | Microservices | MEDIUM | `references/microservices-devops.md` |
|
|
38
|
+
| 10 | DevOps & Deployment | LOW-MEDIUM | `references/microservices-devops.md` |
|
|
39
|
+
|
|
40
|
+
### Step 2: Aplicar + verificar
|
|
41
|
+
|
|
42
|
+
Para cada regla relevante: leer el ejemplo correcto en el reference, aplicarlo, y verificar contra el checklist de cierre.
|
|
43
|
+
|
|
44
|
+
### Step 3: Checklist de cierre
|
|
45
|
+
|
|
46
|
+
- [ ] Sin dependencias circulares (`madge --circular` o equivalente)
|
|
47
|
+
- [ ] Constructor injection en todos los providers; cero `ModuleRef.get()` en runtime
|
|
48
|
+
- [ ] `ValidationPipe` global + DTOs decorados en todos los endpoints
|
|
49
|
+
- [ ] Guards declarativos para auth/authz (no checks manuales)
|
|
50
|
+
- [ ] Sin N+1 (eager loading / joins donde corresponde)
|
|
51
|
+
- [ ] Migraciones versionadas, `synchronize: false` en prod
|
|
52
|
+
- [ ] Graceful shutdown (SIGTERM) + health checks
|
|
53
|
+
- [ ] Structured logging (JSON)
|
|
54
|
+
|
|
55
|
+
Si todo el checklist pasa → código **COMPLIANT**. Antes de escribir cambios significativos, confirmar el approach con el usuario (Question → Decision → Approval).
|
|
56
|
+
|
|
57
|
+
## Tabla de mapeo síntoma → categoría
|
|
58
|
+
|
|
59
|
+
| Síntoma | Categoría |
|
|
60
|
+
|---------|-----------|
|
|
61
|
+
| Crash al arrancar / módulos no resuelven | Architecture, DI |
|
|
62
|
+
| Lentitud en endpoints, muchas queries | Performance, Database |
|
|
63
|
+
| Errores 500 inconsistentes / leaks de stack traces | Error Handling |
|
|
64
|
+
| Endpoints expuestos / brute force / data sensible | Security |
|
|
65
|
+
| Tests frágiles o que llaman servicios reales | Testing |
|
|
66
|
+
| Respuestas inconsistentes / circular refs | API Design |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
_Adaptado de [kadajett/agent-nestjs-skills](https://github.com/kadajett/agent-nestjs-skills). El repo original compila reglas atómicas (`rules/*.md`) a un `AGENTS.md` vía build script; acá se reorganizó al formato skill+references de Arcane._
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 8. API Design (MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 8.1 Use DTOs and Serialization for Responses — MEDIUM
|
|
4
|
+
|
|
5
|
+
Transformar entidades antes de retornar → controlar exposición, evitar referencias circulares, consistencia.
|
|
6
|
+
```ts
|
|
7
|
+
export class UserResponseDto {
|
|
8
|
+
@Expose() id: string;
|
|
9
|
+
@Expose() email: string;
|
|
10
|
+
@Exclude() password: string; // nunca sale
|
|
11
|
+
}
|
|
12
|
+
// @UseInterceptors(ClassSerializerInterceptor)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 8.2 Use Interceptors for Cross-Cutting Concerns — MEDIUM
|
|
16
|
+
|
|
17
|
+
Logging, transformación, formato de respuesta uniforme (`{ data, meta }`) vía interceptors, no repetido en cada handler.
|
|
18
|
+
|
|
19
|
+
## 8.3 Use Pipes for Input Transformation — MEDIUM
|
|
20
|
+
|
|
21
|
+
Pipes built-in (`ParseIntPipe`, `ParseUUIDPipe`) y custom para validar/transformar input.
|
|
22
|
+
```ts
|
|
23
|
+
@Get(':id') findOne(@Param('id', ParseUUIDPipe) id: string) {}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 8.4 Use API Versioning for Breaking Changes — MEDIUM
|
|
27
|
+
|
|
28
|
+
Versionar endpoints para mantener compatibilidad.
|
|
29
|
+
```ts
|
|
30
|
+
app.enableVersioning({ type: VersioningType.URI }); // /v1/users
|
|
31
|
+
@Controller({ path: 'users', version: '1' })
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
_Ref: https://docs.nestjs.com/techniques/serialization · https://docs.nestjs.com/techniques/versioning_
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 1. Architecture (CRITICAL)
|
|
2
|
+
|
|
3
|
+
## 1.1 Avoid Circular Dependencies — CRITICAL
|
|
4
|
+
|
|
5
|
+
Causa #1 de crashes en runtime. `forwardRef()` es un parche, no una solución.
|
|
6
|
+
|
|
7
|
+
**Incorrecto** — A importa B, B importa A:
|
|
8
|
+
```ts
|
|
9
|
+
// users.service.ts → inyecta OrdersService
|
|
10
|
+
// orders.service.ts → inyecta UsersService ⇒ ciclo
|
|
11
|
+
```
|
|
12
|
+
**Correcto** — extraer lo compartido a un tercer módulo o usar eventos:
|
|
13
|
+
```ts
|
|
14
|
+
// Mover la lógica común a SharedModule, o emitir un evento
|
|
15
|
+
this.eventEmitter.emit('order.created', payload);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 1.2 Organize by Feature Modules — CRITICAL
|
|
19
|
+
|
|
20
|
+
Estructurar por feature, no por capa técnica → desarrollo 3-5x más rápido.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
users/ { users.module.ts, users.controller.ts, users.service.ts, dto/ }
|
|
25
|
+
orders/ { orders.module.ts, ... }
|
|
26
|
+
```
|
|
27
|
+
No: `controllers/`, `services/`, `repositories/` globales.
|
|
28
|
+
|
|
29
|
+
## 1.3 Proper Module Sharing — CRITICAL
|
|
30
|
+
|
|
31
|
+
Exportar servicios desde un módulo dedicado garantiza instancia singleton.
|
|
32
|
+
|
|
33
|
+
**Correcto:**
|
|
34
|
+
```ts
|
|
35
|
+
@Module({ providers: [UsersService], exports: [UsersService] })
|
|
36
|
+
export class UsersModule {}
|
|
37
|
+
// Otro módulo: imports: [UsersModule] → reusa la MISMA instancia
|
|
38
|
+
```
|
|
39
|
+
Re-declarar el provider en otro módulo crea una instancia nueva → estado inconsistente.
|
|
40
|
+
|
|
41
|
+
## 1.4 Single Responsibility for Services — CRITICAL
|
|
42
|
+
|
|
43
|
+
Un servicio = un concepto de dominio. +40% testabilidad. Si un servicio hace auth + email + billing, separarlo.
|
|
44
|
+
|
|
45
|
+
## 1.5 Event-Driven Architecture for Decoupling — MEDIUM-HIGH
|
|
46
|
+
|
|
47
|
+
Emitir eventos de dominio (`@nestjs/event-emitter`) para desacoplar módulos y permitir procesamiento async sin dependencias directas.
|
|
48
|
+
|
|
49
|
+
## 1.6 Repository Pattern for Data Access — HIGH
|
|
50
|
+
|
|
51
|
+
Encapsular queries en repositorios; separar lógica de negocio de la persistencia. El servicio depende de una abstracción `UserRepository`, no del ORM directamente.
|
|
52
|
+
|
|
53
|
+
_Ref: https://docs.nestjs.com/modules · https://docs.nestjs.com/fundamentals/circular-dependency_
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 7. Database & ORM (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 7.1 Avoid N+1 Query Problems — HIGH
|
|
4
|
+
|
|
5
|
+
El asesino silencioso de performance. Eager loading con relations o joins.
|
|
6
|
+
|
|
7
|
+
**Incorrecto** — 1 query + N queries en loop:
|
|
8
|
+
```ts
|
|
9
|
+
const orders = await this.orderRepo.find();
|
|
10
|
+
for (const o of orders) o.user = await this.userRepo.findOne(o.userId); // N+1
|
|
11
|
+
```
|
|
12
|
+
**Correcto:**
|
|
13
|
+
```ts
|
|
14
|
+
// Prisma
|
|
15
|
+
prisma.order.findMany({ include: { user: true } });
|
|
16
|
+
// TypeORM
|
|
17
|
+
this.orderRepo.find({ relations: ['user'] });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 7.3 Use Transactions for Multi-Step Operations — MEDIUM-HIGH
|
|
21
|
+
|
|
22
|
+
Operaciones dependientes en una transacción → consistencia ante fallos.
|
|
23
|
+
```ts
|
|
24
|
+
await this.prisma.$transaction(async (tx) => {
|
|
25
|
+
await tx.account.update({ where: { id: from }, data: { balance: { decrement: amt } } });
|
|
26
|
+
await tx.account.update({ where: { id: to }, data: { balance: { increment: amt } } });
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 7.2 Use Database Migrations — MEDIUM-HIGH
|
|
31
|
+
|
|
32
|
+
Versionar cambios de schema con migraciones. **Nunca `synchronize: true` en producción** (puede borrar datos).
|
|
33
|
+
```ts
|
|
34
|
+
TypeOrmModule.forRoot({ synchronize: false, migrationsRun: true });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
_Ref: https://docs.nestjs.com/techniques/database · https://www.prisma.io/docs/orm/prisma-migrate_
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 2. Dependency Injection (CRITICAL)
|
|
2
|
+
|
|
3
|
+
## 2.4 Prefer Constructor Injection — CRITICAL
|
|
4
|
+
|
|
5
|
+
Visibilidad, type safety y testabilidad. Siempre.
|
|
6
|
+
```ts
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class OrdersService {
|
|
9
|
+
constructor(private readonly users: UsersService) {}
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 2.1 Avoid Service Locator Anti-Pattern — HIGH
|
|
14
|
+
|
|
15
|
+
**Incorrecto:**
|
|
16
|
+
```ts
|
|
17
|
+
const svc = this.moduleRef.get(UsersService); // dependencia oculta, no testeable
|
|
18
|
+
```
|
|
19
|
+
**Correcto:** inyectar por constructor (2.4). `ModuleRef` solo para casos dinámicos justificados.
|
|
20
|
+
|
|
21
|
+
## 2.5 Understand Provider Scopes — CRITICAL
|
|
22
|
+
|
|
23
|
+
`DEFAULT` (singleton) por defecto. `REQUEST` scope tiene costo de performance (instancia por request) → usar solo cuando se necesita contexto de request real.
|
|
24
|
+
```ts
|
|
25
|
+
@Injectable({ scope: Scope.REQUEST }) // solo si es necesario
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 2.6 Use Injection Tokens for Interfaces — HIGH
|
|
29
|
+
|
|
30
|
+
Las interfaces de TS no existen en runtime → no se pueden inyectar. Usar símbolo o abstract class como token.
|
|
31
|
+
```ts
|
|
32
|
+
export const USER_REPO = Symbol('USER_REPO');
|
|
33
|
+
@Module({ providers: [{ provide: USER_REPO, useClass: PrismaUserRepository }] })
|
|
34
|
+
// constructor(@Inject(USER_REPO) private repo: UserRepository) {}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 2.2 Interface Segregation — HIGH
|
|
38
|
+
|
|
39
|
+
Interfaces chicas y enfocadas, no "fat interfaces" que fuerzan dependencias sin usar.
|
|
40
|
+
|
|
41
|
+
## 2.3 Liskov Substitution — HIGH
|
|
42
|
+
|
|
43
|
+
Toda implementación de una interfaz honra el contrato idéntico → intercambiables sin romper consumidores.
|
|
44
|
+
|
|
45
|
+
_Ref: https://docs.nestjs.com/fundamentals/custom-providers · https://docs.nestjs.com/fundamentals/injection-scopes_
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 3. Error Handling (HIGH)
|
|
2
|
+
|
|
3
|
+
## 3.2 Throw HTTP Exceptions from Services — HIGH
|
|
4
|
+
|
|
5
|
+
Los servicios lanzan excepciones, no retornan objetos de error.
|
|
6
|
+
|
|
7
|
+
**Incorrecto:**
|
|
8
|
+
```ts
|
|
9
|
+
return { error: 'not found' }; // el controller tiene que adivinar
|
|
10
|
+
```
|
|
11
|
+
**Correcto:**
|
|
12
|
+
```ts
|
|
13
|
+
throw new NotFoundException(`User ${id} not found`);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 3.1 Handle Async Errors Properly — HIGH
|
|
17
|
+
|
|
18
|
+
Fire-and-forget y background tasks deben atrapar errores → si no, unhandled rejection puede tumbar el proceso.
|
|
19
|
+
|
|
20
|
+
**Incorrecto:**
|
|
21
|
+
```ts
|
|
22
|
+
this.mailer.send(email); // floating promise, error perdido
|
|
23
|
+
```
|
|
24
|
+
**Correcto:**
|
|
25
|
+
```ts
|
|
26
|
+
void this.mailer.send(email).catch(err => this.logger.error('mail failed', err));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 3.3 Use Exception Filters — HIGH
|
|
30
|
+
|
|
31
|
+
Filtro global para formato de respuesta de error consistente y centralizado.
|
|
32
|
+
```ts
|
|
33
|
+
@Catch()
|
|
34
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
35
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
36
|
+
// shape único: { statusCode, message, timestamp, path }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// app.useGlobalFilters(new AllExceptionsFilter());
|
|
40
|
+
```
|
|
41
|
+
Nunca filtrar stack traces crudos al cliente en producción.
|
|
42
|
+
|
|
43
|
+
_Ref: https://docs.nestjs.com/exception-filters_
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 9. Microservices (MEDIUM) & 10. DevOps (LOW-MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 9.1 Implement Health Checks — MEDIUM
|
|
4
|
+
|
|
5
|
+
Endpoints readiness/liveness con `@nestjs/terminus` para que el orquestador maneje la salud del servicio.
|
|
6
|
+
```ts
|
|
7
|
+
@Get('health') @HealthCheck()
|
|
8
|
+
check() { return this.health.check([() => this.db.pingCheck('db')]); }
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 9.2 Message and Event Patterns Correctly — MEDIUM
|
|
12
|
+
|
|
13
|
+
Distinguir request-response (`@MessagePattern`) de event-based (`@EventPattern`). Usar el patrón correcto según si se espera respuesta.
|
|
14
|
+
|
|
15
|
+
## 9.3 Use Message Queues for Background Jobs — MEDIUM
|
|
16
|
+
|
|
17
|
+
Offload de tareas largas a colas (`@nestjs/bull` / BullMQ) → no bloquear handlers de request.
|
|
18
|
+
```ts
|
|
19
|
+
await this.reportQueue.add('generate', { userId }); // responde rápido, procesa async
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 10.1 Implement Graceful Shutdown — MEDIUM
|
|
23
|
+
|
|
24
|
+
Escuchar SIGTERM, completar requests in-flight antes de salir.
|
|
25
|
+
```ts
|
|
26
|
+
app.enableShutdownHooks();
|
|
27
|
+
// onModuleDestroy() { await this.drainConnections(); }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 10.2 Use ConfigModule for Environment Config — MEDIUM
|
|
31
|
+
|
|
32
|
+
Config por entorno vía `ConfigModule`, validada al boot. Nada hardcodeado.
|
|
33
|
+
```ts
|
|
34
|
+
ConfigModule.forRoot({ validationSchema: Joi.object({ DATABASE_URL: Joi.string().required() }) });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 10.3 Use Structured Logging — LOW-MEDIUM
|
|
38
|
+
|
|
39
|
+
Logging JSON consistente (pino/winston) con `request_id`, `trace_id` para parseo/filtrado en producción.
|
|
40
|
+
|
|
41
|
+
_Ref: https://docs.nestjs.com/recipes/terminus · https://docs.nestjs.com/microservices/basics · https://docs.nestjs.com/techniques/configuration_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 5. Performance (HIGH)
|
|
2
|
+
|
|
3
|
+
## 5.3 Optimize Database Queries — HIGH
|
|
4
|
+
|
|
5
|
+
Seleccionar solo columnas necesarias, índices apropiados, no over-fetch de relaciones.
|
|
6
|
+
```ts
|
|
7
|
+
// Prisma: select explícito en vez de traer la fila entera + relaciones
|
|
8
|
+
prisma.user.findMany({ select: { id: true, email: true } });
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 5.4 Use Caching Strategically — HIGH
|
|
12
|
+
|
|
13
|
+
Cachear operaciones caras con TTL apropiado e invalidación basada en eventos.
|
|
14
|
+
```ts
|
|
15
|
+
@UseInterceptors(CacheInterceptor)
|
|
16
|
+
@CacheTTL(30)
|
|
17
|
+
@Get('stats') getStats() { /* ... */ }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 5.1 Async Lifecycle Hooks Correctly — HIGH
|
|
21
|
+
|
|
22
|
+
Retornar promesas de hooks async; constructores síncronos (no bloquear el arranque).
|
|
23
|
+
```ts
|
|
24
|
+
async onModuleInit() { await this.warmupCache(); } // ✓
|
|
25
|
+
// constructor(){ await ... } ✗ no se puede / bloquea
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 5.2 Lazy Loading for Large Modules — MEDIUM
|
|
29
|
+
|
|
30
|
+
Diferir inicialización de módulos poco usados con `LazyModuleLoader` → menor tiempo de arranque y memoria.
|
|
31
|
+
|
|
32
|
+
_Ref: https://docs.nestjs.com/techniques/caching · https://docs.nestjs.com/fundamentals/lazy-loading-modules_
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# 4. Security (HIGH)
|
|
2
|
+
|
|
3
|
+
## 4.1 Secure JWT Authentication — CRITICAL
|
|
4
|
+
|
|
5
|
+
Tokens de vida corta (15m) + refresh tokens. Secreto en env/secret manager. Nunca data sensible en el payload.
|
|
6
|
+
```ts
|
|
7
|
+
JwtModule.register({
|
|
8
|
+
secret: process.env.JWT_SECRET, // nunca hardcodeado
|
|
9
|
+
signOptions: { expiresIn: '15m' },
|
|
10
|
+
});
|
|
11
|
+
// payload: { sub: userId, role } ← nada de passwords, emails completos, PII
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 4.5 Validate All Input with DTOs and Pipes — HIGH
|
|
15
|
+
|
|
16
|
+
`ValidationPipe` global + DTOs decorados. Rechazar input malformado antes de procesarlo.
|
|
17
|
+
```ts
|
|
18
|
+
app.useGlobalPipes(new ValidationPipe({
|
|
19
|
+
whitelist: true, // descarta props no declaradas
|
|
20
|
+
forbidNonWhitelisted: true, // 400 si vienen props extra
|
|
21
|
+
transform: true,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
export class CreateUserDto {
|
|
25
|
+
@IsEmail() email: string;
|
|
26
|
+
@MinLength(8) password: string;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 4.4 Use Guards for AuthN/AuthZ — HIGH
|
|
31
|
+
|
|
32
|
+
Guards declarativos, no checks manuales en cada handler.
|
|
33
|
+
```ts
|
|
34
|
+
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
35
|
+
@Roles('admin')
|
|
36
|
+
@Get('users') findAll() { /* ... */ }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 4.2 Rate Limiting — HIGH
|
|
40
|
+
|
|
41
|
+
`@nestjs/throttler`, límites por endpoint (login más estricto que lecturas).
|
|
42
|
+
```ts
|
|
43
|
+
@Throttle({ default: { limit: 5, ttl: 60000 } }) // 5/min en /login
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 4.3 Sanitize Output to Prevent XSS — HIGH
|
|
47
|
+
|
|
48
|
+
Sanitizar contenido generado por usuario antes de almacenar; `Content-Type` correcto en respuestas.
|
|
49
|
+
|
|
50
|
+
_Ref: https://docs.nestjs.com/security/authentication · https://docs.nestjs.com/security/rate-limiting_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 6. Testing (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 6.3 Use Testing Module for Unit Tests — HIGH
|
|
4
|
+
|
|
5
|
+
Entorno aislado con `Test.createTestingModule` y dependencias mockeadas.
|
|
6
|
+
```ts
|
|
7
|
+
const moduleRef = await Test.createTestingModule({
|
|
8
|
+
providers: [
|
|
9
|
+
OrdersService,
|
|
10
|
+
{ provide: UserRepository, useValue: mockUserRepo },
|
|
11
|
+
],
|
|
12
|
+
}).compile();
|
|
13
|
+
const service = moduleRef.get(OrdersService);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 6.2 Mock External Services in Tests — HIGH
|
|
17
|
+
|
|
18
|
+
Nunca llamar APIs o DBs reales en tests. Mockear todas las dependencias externas con escenarios realistas (éxito, error, timeout).
|
|
19
|
+
|
|
20
|
+
## 6.1 Use Supertest for E2E Testing — HIGH
|
|
21
|
+
|
|
22
|
+
Probar el ciclo completo request/response incluyendo middleware, guards y pipes. Setup/teardown apropiados.
|
|
23
|
+
```ts
|
|
24
|
+
const app = moduleRef.createNestApplication();
|
|
25
|
+
await app.init();
|
|
26
|
+
await request(app.getHttpServer())
|
|
27
|
+
.post('/users').send({ email: 'a@b.com', password: 'secret123' })
|
|
28
|
+
.expect(201);
|
|
29
|
+
await app.close();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
_Ref: https://docs.nestjs.com/fundamentals/testing_
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-scaffold
|
|
3
|
+
description: "Scaffold de API NestJS production-ready con módulos por feature, DI, ValidationPipe global, Prisma/Drizzle, guards de auth y testing. Usar para iniciar un backend NestJS nuevo."
|
|
4
|
+
category: "backend"
|
|
5
|
+
argument-hint: "[project-name]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nestjs-scaffold — NestJS API Scaffolder
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar cualquier código, completar estos pasos en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
1. **Nombre del servicio** (kebab-case)
|
|
17
|
+
2. **Transport:** REST (default) / GraphQL / microservicio
|
|
18
|
+
3. **ORM:** Prisma (default) / Drizzle / TypeORM
|
|
19
|
+
4. **DB:** PostgreSQL (default) / MySQL
|
|
20
|
+
5. **Auth:** JWT + refresh (default) / OAuth / ninguna
|
|
21
|
+
6. **Deploy:** Docker (default) / Railway / AWS ECS
|
|
22
|
+
|
|
23
|
+
### Step 1: Crear base
|
|
24
|
+
```bash
|
|
25
|
+
npm i -g @nestjs/cli
|
|
26
|
+
nest new <name> --package-manager pnpm
|
|
27
|
+
cd <name>
|
|
28
|
+
nest g module users && nest g controller users && nest g service users
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Step 2: Estructura (feature modules — NO por capa)
|
|
32
|
+
```
|
|
33
|
+
src/
|
|
34
|
+
users/ { users.module.ts, users.controller.ts, users.service.ts, dto/, entities/ }
|
|
35
|
+
auth/ { auth.module.ts, jwt.strategy.ts, guards/, decorators/ }
|
|
36
|
+
common/ { filters/, interceptors/, pipes/ }
|
|
37
|
+
config/ { config.module.ts, env.validation.ts }
|
|
38
|
+
main.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Step 3: Bootstrap no negociable (`main.ts`)
|
|
42
|
+
```ts
|
|
43
|
+
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }));
|
|
44
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
45
|
+
app.enableShutdownHooks(); // graceful shutdown
|
|
46
|
+
app.enableVersioning({ type: VersioningType.URI });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 4: Defaults
|
|
50
|
+
- Constructor injection siempre; cero `ModuleRef.get()` en runtime
|
|
51
|
+
- DTOs decorados (`class-validator`) en todos los endpoints
|
|
52
|
+
- Guards declarativos para auth/authz
|
|
53
|
+
- `ConfigModule` con validación de env (Joi/Zod) al boot
|
|
54
|
+
- Migraciones versionadas, `synchronize: false`
|
|
55
|
+
- Health check (`@nestjs/terminus`) + structured logging (pino)
|
|
56
|
+
- Tests: `Test.createTestingModule` (unit) + supertest (e2e)
|
|
57
|
+
|
|
58
|
+
### Step 5: Verificar
|
|
59
|
+
`npm run build` + `npm run test`. Revisar contra la rule `nestjs-code` y el skill `nestjs-best-practices`. Build + tests verdes → scaffold **READY**.
|
|
60
|
+
|
|
61
|
+
## Stack Defaults
|
|
62
|
+
|
|
63
|
+
| Componente | Default |
|
|
64
|
+
|------------|---------|
|
|
65
|
+
| Framework | NestJS 10+ |
|
|
66
|
+
| ORM | Prisma |
|
|
67
|
+
| DB | PostgreSQL |
|
|
68
|
+
| Auth | JWT + refresh tokens |
|
|
69
|
+
| Validación | class-validator + ValidationPipe |
|
|
70
|
+
| Testing | Jest + supertest |
|
|
71
|
+
| Deploy | Docker |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
_Inspirado en las skills NestJS de [kadajett/agent-nestjs-skills](https://github.com/kadajett/agent-nestjs-skills) y [giuseppe-trisciuoglio/developer-kit](https://github.com/giuseppe-trisciuoglio/developer-kit). Adaptado al formato Arcane._
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-best-practices
|
|
3
|
+
description: "React + Next.js performance: 64 reglas en 8 categorías (waterfalls, bundle, server, client fetching, re-render, rendering, JS, advanced) priorizadas por impacto, de Vercel Engineering. Usar al escribir/optimizar React o Next.js."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[waterfalls|bundle|server|rerender|rendering|js|all]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nextjs-best-practices — Performance priorizada por impacto
|
|
10
|
+
|
|
11
|
+
64 reglas de optimización de performance para React y Next.js, de Vercel Engineering, organizadas en 8 categorías priorizadas de **CRITICAL** a **LOW**. Detalle por categoría en `references/`.
|
|
12
|
+
|
|
13
|
+
## MANDATORY WORKFLOW
|
|
14
|
+
|
|
15
|
+
**Diagnóstico dirigido por métricas, no aplicar las 64 reglas a ciegas** (este es el enfoque `vercel-optimize`: medir primero, optimizar solo lo que las métricas señalan).
|
|
16
|
+
|
|
17
|
+
### Step 0: Medir / localizar
|
|
18
|
+
|
|
19
|
+
1. **¿Hay métricas?** Lighthouse, Web Vitals (LCP/INP/CLS), bundle analyzer, traces de Vercel.
|
|
20
|
+
2. **¿Cuál es el síntoma dominante?** TTFB alto → waterfalls/server. Bundle grande / TBT → bundle size. Jank en interacción → re-render. Layout shift → rendering.
|
|
21
|
+
3. Identificar las rutas/componentes concretos que las métricas apuntan. Optimizar **esos**, no todo.
|
|
22
|
+
|
|
23
|
+
### Step 1: Priorizar por impacto
|
|
24
|
+
|
|
25
|
+
| Prioridad | Categoría | Impacto | Reference |
|
|
26
|
+
|-----------|-----------|---------|-----------|
|
|
27
|
+
| 1 | Eliminating Waterfalls | CRITICAL | `references/waterfalls.md` |
|
|
28
|
+
| 2 | Bundle Size Optimization | CRITICAL | `references/bundle-size.md` |
|
|
29
|
+
| 3 | Server-Side Performance | HIGH | `references/server.md` |
|
|
30
|
+
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `references/client-fetching.md` |
|
|
31
|
+
| 5 | Re-render Optimization | MEDIUM | `references/rerender.md` |
|
|
32
|
+
| 6 | Rendering Performance | MEDIUM | `references/rendering.md` |
|
|
33
|
+
| 7 | JavaScript Performance | LOW-MEDIUM | `references/javascript.md` |
|
|
34
|
+
| 8 | Advanced Patterns | LOW | `references/advanced.md` |
|
|
35
|
+
|
|
36
|
+
### Step 2: Aplicar + medir de nuevo
|
|
37
|
+
|
|
38
|
+
Aplicar la regla, re-medir. Si la métrica no mejoró, revertir y subir de categoría — no acumular micro-optimizaciones sin impacto medible.
|
|
39
|
+
|
|
40
|
+
### Step 3: Checklist de cierre
|
|
41
|
+
|
|
42
|
+
- [ ] Fetches independientes en paralelo (`Promise.all`), no en cascada
|
|
43
|
+
- [ ] Server Components por defecto; `'use client'` empujado abajo en el árbol
|
|
44
|
+
- [ ] Sin barrel imports en el critical path; componentes pesados con `next/dynamic`
|
|
45
|
+
- [ ] Server Actions autenticadas individualmente
|
|
46
|
+
- [ ] Sin componentes definidos dentro de componentes
|
|
47
|
+
- [ ] `next/image` + `generateMetadata` + resource hints donde corresponde
|
|
48
|
+
- [ ] Re-medir Web Vitals post-cambios
|
|
49
|
+
|
|
50
|
+
Si todo el checklist pasa → optimización **COMPLETE**. Antes de escribir cambios significativos, confirmar el approach con el usuario (Question → Decision → Approval).
|
|
51
|
+
|
|
52
|
+
## Tabla síntoma → categoría
|
|
53
|
+
|
|
54
|
+
| Métrica mala | Categoría |
|
|
55
|
+
|--------------|-----------|
|
|
56
|
+
| TTFB / tiempo de respuesta del server | Waterfalls, Server |
|
|
57
|
+
| First Load JS / TBT alto | Bundle Size |
|
|
58
|
+
| INP / jank en interacción | Re-render, JS |
|
|
59
|
+
| LCP | Rendering, Bundle, Server |
|
|
60
|
+
| CLS / flicker en hydration | Rendering |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
_Adaptado de [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) (react-best-practices, MIT, © Vercel). 64 reglas priorizadas por impacto, reorganizadas al formato skill+references de Arcane._
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 8. Advanced Patterns (LOW)
|
|
2
|
+
|
|
3
|
+
Patrones de borde con `useEffectEvent` y setup de app. Impacto bajo pero evitan bugs sutiles.
|
|
4
|
+
|
|
5
|
+
## 8.4 useEffectEvent for Stable Callback Refs — LOW
|
|
6
|
+
Referencias de callback estables para componentes memoizados sin re-disparar effects.
|
|
7
|
+
```tsx
|
|
8
|
+
const onTick = useEffectEvent(() => doSomething(latestProp));
|
|
9
|
+
useEffect(() => { const id = setInterval(onTick, 1000); return () => clearInterval(id); }, []);
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 8.1 Do Not Put Effect Events in Dependency Arrays — LOW
|
|
13
|
+
Los Effect Events no van en el array de dependencias; se llaman desde el effect, no lo re-disparan.
|
|
14
|
+
|
|
15
|
+
## 8.2 Initialize App Once, Not Per Mount — LOW
|
|
16
|
+
Setup de app a nivel module-load, no en effects/mounts de componente.
|
|
17
|
+
|
|
18
|
+
## 8.3 Store Event Handlers in Refs — LOW
|
|
19
|
+
Handlers que cambian seguido en refs → no causan re-render.
|
|
20
|
+
|
|
21
|
+
_Ref: https://react.dev/reference/react/experimental_useEffectEvent_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 2. Bundle Size Optimization (CRITICAL)
|
|
2
|
+
|
|
3
|
+
## 2.1 Avoid Barrel File Imports — CRITICAL
|
|
4
|
+
Los barrels (`index.ts` que re-exportan todo) arrastran miles de módulos sin usar.
|
|
5
|
+
```ts
|
|
6
|
+
// ✗ import { debounce } from 'lodash'; (todo lodash)
|
|
7
|
+
// ✓ import debounce from 'lodash/debounce';
|
|
8
|
+
```
|
|
9
|
+
Configurar `optimizePackageImports` en `next.config.js` para libs grandes.
|
|
10
|
+
|
|
11
|
+
## 2.4 Dynamic Imports for Heavy Components — CRITICAL
|
|
12
|
+
```tsx
|
|
13
|
+
const Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () => <Skeleton/> });
|
|
14
|
+
```
|
|
15
|
+
Para charts, editores de texto rico, modales, mapas — todo lo no necesario en el primer render.
|
|
16
|
+
|
|
17
|
+
## 2.5 Prefer Statically Analyzable Paths — HIGH
|
|
18
|
+
Imports con paths literales/mapas explícitos, no construidos en runtime → permite tree-shaking.
|
|
19
|
+
|
|
20
|
+
## 2.2 Conditional Module Loading — HIGH
|
|
21
|
+
Cargar data/módulos grandes solo cuando la feature se activa.
|
|
22
|
+
|
|
23
|
+
## 2.6 Preload Based on User Intent — MEDIUM
|
|
24
|
+
Precargar el bundle pesado en hover/focus, antes de que se necesite.
|
|
25
|
+
```tsx
|
|
26
|
+
<button onMouseEnter={() => import('./HeavyModal')}>Abrir</button>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 2.3 Defer Non-Critical Third-Party Libraries — MEDIUM
|
|
30
|
+
Analytics, logging, error tracking → cargar después de hydration, no bloquear el bundle inicial (`next/script` con `strategy="afterInteractive"` o `lazyOnload`).
|
|
31
|
+
|
|
32
|
+
_Ref: https://nextjs.org/docs/app/api-reference/components/script · https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports_
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 4.3 Use SWR for Automatic Deduplication — MEDIUM-HIGH
|
|
4
|
+
Dedup de requests, caching y revalidación entre instancias de componente.
|
|
5
|
+
```ts
|
|
6
|
+
const { data } = useSWR(`/api/user/${id}`, fetcher); // mismo key = 1 request
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 4.2 Use Passive Event Listeners for Scrolling — MEDIUM
|
|
10
|
+
```ts
|
|
11
|
+
el.addEventListener('touchstart', handler, { passive: true }); // scroll inmediato
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 4.4 Version and Minimize localStorage Data — MEDIUM
|
|
15
|
+
Prefijo de versión en las keys (`app:v2:prefs`) y guardar solo campos necesarios → evita conflictos y reduce tamaño.
|
|
16
|
+
|
|
17
|
+
## 4.1 Deduplicate Global Event Listeners — LOW
|
|
18
|
+
Compartir listeners globales entre instancias con un `Map` + `useSWRSubscription()` en vez de uno por componente.
|
|
19
|
+
|
|
20
|
+
_Ref: https://swr.vercel.app_
|