claude-code-arcane 1.0.0 → 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 +14 -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/dist/cli.js +105 -6
- package/docs/trusted-publishing-setup.md +163 -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,56 @@
|
|
|
1
|
+
name: backend-nextjs
|
|
2
|
+
description: "Next.js full-stack — App Router, RSC, Server Actions, SEO, AI SDK, performance (64 reglas de Vercel). Front + back en un solo runtime."
|
|
3
|
+
type: base
|
|
4
|
+
|
|
5
|
+
skills:
|
|
6
|
+
# Next.js-specific (nuevas)
|
|
7
|
+
- nextjs-scaffold
|
|
8
|
+
- nextjs-best-practices
|
|
9
|
+
- seo-nextjs
|
|
10
|
+
- ai-sdk-setup
|
|
11
|
+
- pgvector-search
|
|
12
|
+
# Backend / API (Next es full-stack)
|
|
13
|
+
- api-design
|
|
14
|
+
- database
|
|
15
|
+
- caching-strategy
|
|
16
|
+
- rate-limiting
|
|
17
|
+
# Auth & security
|
|
18
|
+
- auth-strategy
|
|
19
|
+
- jwt-strategy
|
|
20
|
+
- oauth-setup
|
|
21
|
+
- web-security
|
|
22
|
+
- csp-headers
|
|
23
|
+
# Frontend quality
|
|
24
|
+
- accessibility
|
|
25
|
+
- performance
|
|
26
|
+
# Ops & quality
|
|
27
|
+
- testing
|
|
28
|
+
- deps-audit
|
|
29
|
+
- env-sync
|
|
30
|
+
- observability
|
|
31
|
+
- ci-cd-setup
|
|
32
|
+
- cdn-setup
|
|
33
|
+
|
|
34
|
+
rules:
|
|
35
|
+
universal:
|
|
36
|
+
- nextjs-code
|
|
37
|
+
- react-perf
|
|
38
|
+
- frontend-code
|
|
39
|
+
- backend-code
|
|
40
|
+
- api-code
|
|
41
|
+
gamedev:
|
|
42
|
+
[]
|
|
43
|
+
|
|
44
|
+
agents:
|
|
45
|
+
- engineering
|
|
46
|
+
|
|
47
|
+
permissions:
|
|
48
|
+
allow:
|
|
49
|
+
- "Bash(npm *)"
|
|
50
|
+
- "Bash(yarn *)"
|
|
51
|
+
- "Bash(pnpm *)"
|
|
52
|
+
- "Bash(npx *)"
|
|
53
|
+
- "Bash(docker ps*)"
|
|
54
|
+
- "Bash(docker images*)"
|
|
55
|
+
deny:
|
|
56
|
+
[]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "src/**/*.ts"
|
|
4
|
+
- "**/*.module.ts"
|
|
5
|
+
- "**/*.controller.ts"
|
|
6
|
+
- "**/*.service.ts"
|
|
7
|
+
- "**/*.guard.ts"
|
|
8
|
+
- "**/*.pipe.ts"
|
|
9
|
+
- "**/*.interceptor.ts"
|
|
10
|
+
- "**/*.dto.ts"
|
|
11
|
+
- "apps/**"
|
|
12
|
+
- "libs/**"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# NestJS Code Rules
|
|
16
|
+
|
|
17
|
+
Reglas production-ready para NestJS, ordenadas por impacto. Catálogo completo (40 reglas, 10 categorías) + ejemplos incorrecto/correcto: skill `nestjs-best-practices`.
|
|
18
|
+
|
|
19
|
+
## CRITICAL — Arquitectura & DI
|
|
20
|
+
|
|
21
|
+
- **Cero dependencias circulares entre módulos** — causa #1 de crashes en runtime. Si aparece `forwardRef()`, es señal de mal diseño: refactorizar.
|
|
22
|
+
- **Organizar por feature modules**, no por capas técnicas (`users/`, no `controllers/` + `services/` globales).
|
|
23
|
+
- **Exportar servicios desde módulos dedicados** para garantizar instancias singleton y evitar estado inconsistente.
|
|
24
|
+
- **Single Responsibility por servicio** — un servicio = un concepto de dominio.
|
|
25
|
+
- **Constructor injection siempre.** Nunca `ModuleRef.get()` en runtime (service locator anti-pattern). Dependencias explícitas, tipadas, testeables.
|
|
26
|
+
- **Provider scopes**: `DEFAULT` (singleton) por defecto; `REQUEST` scope solo cuando se necesita contexto de request (tiene costo de performance).
|
|
27
|
+
- **Injection tokens (símbolos o abstract classes) para interfaces** — las interfaces de TS no existen en runtime.
|
|
28
|
+
|
|
29
|
+
## HIGH — Error handling & Security
|
|
30
|
+
|
|
31
|
+
- **Servicios lanzan `HttpException` subclasses**, no retornan objetos de error.
|
|
32
|
+
- **Manejar errores async explícitamente** — fire-and-forget y background tasks deben atrapar errores (no unhandled rejections).
|
|
33
|
+
- **Exception filters** para formato de respuesta de error centralizado y consistente.
|
|
34
|
+
- **`ValidationPipe` global + DTOs decorados** — rechazar input malformado antes de procesarlo. `whitelist: true`, `forbidNonWhitelisted: true`.
|
|
35
|
+
- **Guards declarativos para auth/authz**, no chequeos manuales en cada handler.
|
|
36
|
+
- **JWT seguro**: tokens de vida corta + refresh tokens, secretos en env/secret manager, nunca data sensible en el payload.
|
|
37
|
+
- **Rate limiting con `@nestjs/throttler`**, límites por endpoint.
|
|
38
|
+
- **Sanitizar output** (XSS) y setear `Content-Type` correcto.
|
|
39
|
+
|
|
40
|
+
## HIGH — Performance & Data
|
|
41
|
+
|
|
42
|
+
- **Evitar N+1**: eager loading con relations o joins de QueryBuilder.
|
|
43
|
+
- **Optimizar queries**: seleccionar solo columnas necesarias, índices apropiados, no over-fetch de relaciones.
|
|
44
|
+
- **Caching estratégico** con TTL apropiado e invalidación basada en eventos.
|
|
45
|
+
- **Transacciones para operaciones multi-step** dependientes (consistencia ante fallos).
|
|
46
|
+
- **Migraciones versionadas** — nunca `synchronize: true` en producción.
|
|
47
|
+
- **Async lifecycle hooks** retornan promesas; constructores síncronos (no bloquear el arranque).
|
|
48
|
+
|
|
49
|
+
## MEDIUM — API, Microservices, DevOps
|
|
50
|
+
|
|
51
|
+
- **DTOs + serialización** (`ClassSerializerInterceptor`) para respuestas — controlar exposición, evitar referencias circulares.
|
|
52
|
+
- **Interceptors para cross-cutting concerns** (logging, transformación, formato de respuesta).
|
|
53
|
+
- **Pipes para transformación/validación** de input.
|
|
54
|
+
- **API versioning** para cambios breaking.
|
|
55
|
+
- **Health checks** (readiness/liveness) para orquestadores.
|
|
56
|
+
- **Message queues para jobs largos** — no bloquear handlers de request.
|
|
57
|
+
- **Graceful shutdown**: escuchar SIGTERM, drenar requests in-flight antes de salir.
|
|
58
|
+
- **`ConfigModule` para configuración** por entorno — nada hardcodeado.
|
|
59
|
+
- **Structured logging (JSON)** para parseo/filtrado en producción.
|
|
60
|
+
|
|
61
|
+
## Anti-Patterns
|
|
62
|
+
|
|
63
|
+
- `forwardRef()` para parchear dependencias circulares en vez de rediseñar
|
|
64
|
+
- Lógica de negocio en controllers (solo parsean input, llaman service, dan shape a la respuesta)
|
|
65
|
+
- `synchronize: true` contra una DB de producción
|
|
66
|
+
- `ModuleRef.get()` / service locator en runtime
|
|
67
|
+
- Validación manual de input en cada handler en vez de `ValidationPipe` + DTO
|
|
68
|
+
- Devolver entidades de ORM crudas como respuesta HTTP
|
|
69
|
+
- `REQUEST` scope sin necesidad real (penaliza performance)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
_Derivado de [kadajett/agent-nestjs-skills](https://github.com/kadajett/agent-nestjs-skills) (nestjs-best-practices). Condensado al formato Arcane._
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "app/**"
|
|
4
|
+
- "src/app/**"
|
|
5
|
+
- "pages/**"
|
|
6
|
+
- "src/pages/**"
|
|
7
|
+
- "**/*.tsx"
|
|
8
|
+
- "**/route.ts"
|
|
9
|
+
- "next.config.*"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Next.js Code Rules
|
|
13
|
+
|
|
14
|
+
Reglas de performance y arquitectura para Next.js App Router (RSC, data fetching, caching). Ordenadas por impacto. Catálogo completo + ejemplos: skill `nextjs-best-practices`.
|
|
15
|
+
|
|
16
|
+
## CRITICAL — Eliminar waterfalls
|
|
17
|
+
|
|
18
|
+
- **Server Components por defecto.** `'use client'` solo cuando se necesita interactividad/estado/efectos/browser APIs. Empujar el boundary client lo más abajo posible en el árbol.
|
|
19
|
+
- **Fetch paralelo, no secuencial.** Operaciones independientes con `Promise.all()`. Nunca `await` en serie cuando no hay dependencia entre llamadas.
|
|
20
|
+
- **Iniciar promesas temprano, await tarde.** En route handlers y RSC, disparar el fetch independiente al inicio y await recién donde se usa el dato.
|
|
21
|
+
- **Composición para fetch paralelo.** Reestructurar componentes para que fetches independientes corran simultáneamente; fetches dependientes se anidan dentro de la promesa de cada item, no en serie global.
|
|
22
|
+
- **Suspense boundaries estratégicos.** Envolver subárboles con `<Suspense>` para mostrar shell rápido mientras streamea el contenido.
|
|
23
|
+
|
|
24
|
+
## CRITICAL — Bundle size
|
|
25
|
+
|
|
26
|
+
- **No barrel imports.** Importar directo del archivo fuente (`lodash/debounce`, no `lodash`); los barrels (`index.ts` que re-exportan todo) arrastran miles de módulos sin usar.
|
|
27
|
+
- **`next/dynamic` para componentes pesados** no necesarios en el render inicial (charts, editores, modales).
|
|
28
|
+
- **Diferir libs third-party no críticas** (analytics, logging, error tracking) a post-hydration; no bloquear el bundle inicial.
|
|
29
|
+
- **Paths estáticamente analizables.** Imports con paths literales/mapas explícitos, no construidos dinámicamente.
|
|
30
|
+
|
|
31
|
+
## HIGH — Server
|
|
32
|
+
|
|
33
|
+
- **Autenticar cada Server Action como un API route.** Verificar auth/authz dentro de la action, no confiar solo en middleware.
|
|
34
|
+
- **No module-level mutable state para datos de request.** Mantener datos de request locales al árbol de render; las variables a nivel módulo se comparten entre requests → data leaks.
|
|
35
|
+
- **Minimizar serialización en boundaries RSC→Client.** Pasar solo los campos que el cliente realmente usa, no el objeto entero.
|
|
36
|
+
- **`React.cache()` para dedupe por request**; LRU cache para datos compartidos entre requests dentro de una ventana de tiempo.
|
|
37
|
+
- **`after()` para trabajo no bloqueante** (logging, analytics, cleanup) que corre después de enviar la respuesta.
|
|
38
|
+
- **Hoist I/O estático a nivel módulo** para evitar lecturas redundantes en cada request.
|
|
39
|
+
|
|
40
|
+
## HIGH — Rendering & data
|
|
41
|
+
|
|
42
|
+
- **`generateMetadata` para todo el SEO** (title, description, OG, canonical). Nunca tags manuales en el body.
|
|
43
|
+
- **`next/image` siempre** — width/height explícitos, `priority` para LCP, formatos modernos.
|
|
44
|
+
- **Caching explícito.** Conocer y declarar `fetch` cache (`force-cache` / `no-store` / `revalidate`), `revalidatePath`/`revalidateTag` para invalidación.
|
|
45
|
+
- **`content-visibility: auto`** para listas largas off-screen.
|
|
46
|
+
- **Resource hints** (`preload`, `preconnect`) para recursos críticos.
|
|
47
|
+
|
|
48
|
+
## Anti-Patterns
|
|
49
|
+
|
|
50
|
+
- `'use client'` en la raíz del árbol (mata todo el beneficio de RSC)
|
|
51
|
+
- `useEffect` + `fetch` en el cliente para datos que podían venir del server
|
|
52
|
+
- Fetches en cascada (cada componente espera al padre sin necesidad)
|
|
53
|
+
- `export *` barrel files en libs internas
|
|
54
|
+
- Pasar objetos de DB crudos como props a Client Components
|
|
55
|
+
- Hardcodear `revalidate`/cache sin entender la estrategia
|
|
56
|
+
- Secrets o lógica sensible en código que cruza a `'use client'`
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
_Derivado de [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) (react-best-practices, MIT). Condensado al formato Arcane._
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.tsx"
|
|
4
|
+
- "**/*.jsx"
|
|
5
|
+
- "src/components/**"
|
|
6
|
+
- "src/hooks/**"
|
|
7
|
+
- "components/**"
|
|
8
|
+
- "hooks/**"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# React Performance Rules
|
|
12
|
+
|
|
13
|
+
Reglas de re-render, rendering y micro-optimización JS para React. Ordenadas por impacto. Catálogo completo (64 reglas) + ejemplos: skill `nextjs-best-practices`.
|
|
14
|
+
|
|
15
|
+
## HIGH — Estructura de componentes
|
|
16
|
+
|
|
17
|
+
- **No definir componentes dentro de componentes.** Definir a nivel módulo o extraer; definirlos inline los remonta en cada render (pierde estado, mata performance).
|
|
18
|
+
- **`content-visibility: auto`** para listas largas — difiere render y paint de lo que está off-screen.
|
|
19
|
+
- **`defer`/`async` en `<script>`** para no bloquear el render durante el parsing.
|
|
20
|
+
- **Resource hints de React DOM** (`preload`, `preconnect`, `prefetchDNS`) para recursos críticos.
|
|
21
|
+
|
|
22
|
+
## MEDIUM — Re-render optimization
|
|
23
|
+
|
|
24
|
+
- **Calcular derived state durante el render**, no guardarlo en `useState` (se desincroniza y genera renders extra).
|
|
25
|
+
- **Leer state dinámico en el callback**, no suscribirse en render time cuando solo se usa en un handler.
|
|
26
|
+
- **Functional `setState`** (`setX(prev => ...)`) para operar sobre el último valor y crear callbacks estables.
|
|
27
|
+
- **Lazy state init**: pasar función a `useState(() => expensive())` para no recomputar en cada render.
|
|
28
|
+
- **`useTransition` para updates no urgentes**; `useDeferredValue` para mantener inputs responsivos durante cómputo pesado.
|
|
29
|
+
- **`useRef` para valores transitorios** (no-UI) que cambian seguido y no deben disparar render.
|
|
30
|
+
- **Extraer defaults no-primitivos** de componentes memoizados a constantes a nivel módulo (preserva la memoización).
|
|
31
|
+
- **Side effects de interacción van en event handlers**, no en `useEffect`.
|
|
32
|
+
- **Dependencias de effect primitivas y estrechas**, no objetos enteros.
|
|
33
|
+
|
|
34
|
+
## LOW-MEDIUM — useMemo / JS
|
|
35
|
+
|
|
36
|
+
- **No envolver expresiones simples en `useMemo`** — el overhead del hook supera el cómputo.
|
|
37
|
+
- **Index maps (`Map`) para lookups repetidos** en vez de `.find()`/`.includes()` en loops → O(1).
|
|
38
|
+
- **`.flatMap()`** en lugar de `.map().filter(Boolean)`; combinar iteraciones múltiples de array en un solo loop.
|
|
39
|
+
- **Hoist de `RegExp`** a nivel módulo (no recrear en cada render).
|
|
40
|
+
- **Early return** cuando el resultado ya está determinado; chequear `length` antes de operaciones caras (sort).
|
|
41
|
+
- **`.toSorted()`** en vez de `.sort()` para inmutabilidad.
|
|
42
|
+
|
|
43
|
+
## Hydration
|
|
44
|
+
|
|
45
|
+
- Evitar mismatch sin flicker con scripts inline síncronos cuando se actualiza el DOM antes de hidratar.
|
|
46
|
+
- `suppressHydrationWarning` solo para diferencias server/client intencionales (fechas, IDs).
|
|
47
|
+
- Conditional rendering explícito con ternario, no `&&` (evita renderizar `0` u otros falsy).
|
|
48
|
+
|
|
49
|
+
## Anti-Patterns
|
|
50
|
+
|
|
51
|
+
- Componentes definidos dentro del render de otro componente
|
|
52
|
+
- `useMemo`/`useCallback` por default "por las dudas" sin medir
|
|
53
|
+
- `useEffect` para lógica que pertenece a un event handler
|
|
54
|
+
- Guardar en state lo que se puede derivar de props/state existentes
|
|
55
|
+
- `.find()` dentro de `.map()` (O(n²)) sobre listas grandes
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
_Derivado de [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) (react-best-practices, MIT). Condensado al formato Arcane._
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-sdk-setup
|
|
3
|
+
description: "Integrar features de IA en Next.js con el Vercel AI SDK: streaming de chat, tool calling, structured output, agents y RAG. Default a modelos Claude. Usar al agregar chat, generación o agentes a una app Next/Node."
|
|
4
|
+
category: "ai"
|
|
5
|
+
argument-hint: "[chat|stream|tools|structured|agent|rag]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# ai-sdk-setup — Vercel AI SDK con Claude
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
> **Antes de codear features LLM, leer la skill `claude-api`** para model IDs vigentes, params y pricing. No asumir de memoria.
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
1. **Caso:** chat / generación one-shot / extracción structured / agente con tools / RAG
|
|
17
|
+
2. **Streaming** al cliente o respuesta completa
|
|
18
|
+
3. **Modelo:** Opus 4.8 (`claude-opus-4-8`, razonamiento) / Sonnet 4.6 (`claude-sonnet-4-6`, balance) / Haiku 4.5 (`claude-haiku-4-5-20251001`, rápido/barato)
|
|
19
|
+
4. ¿Necesita tool calling / structured output / RAG (ver skill `pgvector-search`)?
|
|
20
|
+
|
|
21
|
+
### Step 1: Instalar
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add ai @ai-sdk/anthropic zod
|
|
24
|
+
# env: ANTHROPIC_API_KEY (validar con Zod en src/lib/env.ts, nunca hardcodear)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Chat con streaming (Route Handler)
|
|
28
|
+
```ts
|
|
29
|
+
// app/api/chat/route.ts
|
|
30
|
+
import { anthropic } from '@ai-sdk/anthropic';
|
|
31
|
+
import { streamText, convertToModelMessages } from 'ai';
|
|
32
|
+
|
|
33
|
+
export async function POST(req: Request) {
|
|
34
|
+
const { messages } = await req.json();
|
|
35
|
+
const result = streamText({
|
|
36
|
+
model: anthropic('claude-sonnet-4-6'),
|
|
37
|
+
messages: convertToModelMessages(messages),
|
|
38
|
+
});
|
|
39
|
+
return result.toUIMessageStreamResponse();
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
Cliente: `useChat()` de `@ai-sdk/react`.
|
|
43
|
+
|
|
44
|
+
### Step 3: Structured output (Zod)
|
|
45
|
+
```ts
|
|
46
|
+
const { object } = await generateObject({
|
|
47
|
+
model: anthropic('claude-sonnet-4-6'),
|
|
48
|
+
schema: z.object({ sentiment: z.enum(['pos','neg','neutral']), score: z.number() }),
|
|
49
|
+
prompt: text,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Step 4: Tool calling / agente
|
|
54
|
+
```ts
|
|
55
|
+
const result = await generateText({
|
|
56
|
+
model: anthropic('claude-opus-4-8'),
|
|
57
|
+
tools: {
|
|
58
|
+
search: tool({
|
|
59
|
+
description: 'Buscar docs', inputSchema: z.object({ q: z.string() }),
|
|
60
|
+
execute: async ({ q }) => searchDocs(q),
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
stopWhen: stepCountIs(5), // loop multi-step controlado
|
|
64
|
+
prompt,
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 5: Buenas prácticas
|
|
69
|
+
- **Server-only** la API key — nunca exponer al cliente
|
|
70
|
+
- Auth en el route handler (como cualquier API route — ver rule `nextjs-code`)
|
|
71
|
+
- Rate limiting en endpoints de IA (costo $)
|
|
72
|
+
- Prompt caching para system prompts grandes (ver skill `claude-api`)
|
|
73
|
+
- Manejar errores de stream y abort (`AbortController`)
|
|
74
|
+
- Validar/parsear toda salida structured con Zod antes de usarla
|
|
75
|
+
|
|
76
|
+
## Cierre
|
|
77
|
+
|
|
78
|
+
Probar el endpoint con streaming real, verificar manejo de errores/abort y que la API key nunca llega al cliente. Endpoint funcionando + key server-only → integración **READY**. Confirmar el approach con el usuario antes de escribir código. Siguiente paso: skill `claude-api` para tuning de modelo/caching, o `pgvector-search` para RAG.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
_Inspirado en las skills de AI SDK de [laguagu/claude-code-nextjs-skills](https://github.com/laguagu/claude-code-nextjs-skills). Adaptado al formato Arcane y a modelos Claude. Verificar API/SDK contra la skill `claude-api`._
|
|
@@ -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_
|