claudient 0.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.
Files changed (283) hide show
  1. package/.claude-plugin/plugin.json +42 -0
  2. package/CONTEXT.md +58 -0
  3. package/README.md +165 -0
  4. package/agents/build-resolvers/de/python-resolver.md +64 -0
  5. package/agents/build-resolvers/de/typescript-resolver.md +65 -0
  6. package/agents/build-resolvers/es/python-resolver.md +64 -0
  7. package/agents/build-resolvers/es/typescript-resolver.md +65 -0
  8. package/agents/build-resolvers/fr/python-resolver.md +64 -0
  9. package/agents/build-resolvers/fr/typescript-resolver.md +65 -0
  10. package/agents/build-resolvers/nl/python-resolver.md +64 -0
  11. package/agents/build-resolvers/nl/typescript-resolver.md +65 -0
  12. package/agents/build-resolvers/python-resolver.md +62 -0
  13. package/agents/build-resolvers/typescript-resolver.md +63 -0
  14. package/agents/core/architect.md +64 -0
  15. package/agents/core/code-reviewer.md +78 -0
  16. package/agents/core/de/architect.md +66 -0
  17. package/agents/core/de/code-reviewer.md +80 -0
  18. package/agents/core/de/planner.md +63 -0
  19. package/agents/core/de/security-reviewer.md +93 -0
  20. package/agents/core/es/architect.md +66 -0
  21. package/agents/core/es/code-reviewer.md +80 -0
  22. package/agents/core/es/planner.md +63 -0
  23. package/agents/core/es/security-reviewer.md +93 -0
  24. package/agents/core/fr/architect.md +66 -0
  25. package/agents/core/fr/code-reviewer.md +80 -0
  26. package/agents/core/fr/planner.md +63 -0
  27. package/agents/core/fr/security-reviewer.md +93 -0
  28. package/agents/core/nl/architect.md +66 -0
  29. package/agents/core/nl/code-reviewer.md +80 -0
  30. package/agents/core/nl/planner.md +63 -0
  31. package/agents/core/nl/security-reviewer.md +93 -0
  32. package/agents/core/planner.md +61 -0
  33. package/agents/core/security-reviewer.md +91 -0
  34. package/guides/agent-orchestration.md +231 -0
  35. package/guides/de/agent-orchestration.md +174 -0
  36. package/guides/de/getting-started.md +164 -0
  37. package/guides/de/hooks-cookbook.md +160 -0
  38. package/guides/de/memory-management.md +153 -0
  39. package/guides/de/security.md +180 -0
  40. package/guides/de/skill-authoring.md +214 -0
  41. package/guides/de/token-optimization.md +156 -0
  42. package/guides/es/agent-orchestration.md +174 -0
  43. package/guides/es/getting-started.md +164 -0
  44. package/guides/es/hooks-cookbook.md +160 -0
  45. package/guides/es/memory-management.md +153 -0
  46. package/guides/es/security.md +180 -0
  47. package/guides/es/skill-authoring.md +214 -0
  48. package/guides/es/token-optimization.md +156 -0
  49. package/guides/fr/agent-orchestration.md +174 -0
  50. package/guides/fr/getting-started.md +164 -0
  51. package/guides/fr/hooks-cookbook.md +227 -0
  52. package/guides/fr/memory-management.md +169 -0
  53. package/guides/fr/security.md +180 -0
  54. package/guides/fr/skill-authoring.md +214 -0
  55. package/guides/fr/token-optimization.md +158 -0
  56. package/guides/getting-started.md +164 -0
  57. package/guides/hooks-cookbook.md +423 -0
  58. package/guides/memory-management.md +192 -0
  59. package/guides/nl/agent-orchestration.md +174 -0
  60. package/guides/nl/getting-started.md +164 -0
  61. package/guides/nl/hooks-cookbook.md +160 -0
  62. package/guides/nl/memory-management.md +153 -0
  63. package/guides/nl/security.md +180 -0
  64. package/guides/nl/skill-authoring.md +214 -0
  65. package/guides/nl/token-optimization.md +156 -0
  66. package/guides/security.md +229 -0
  67. package/guides/skill-authoring.md +226 -0
  68. package/guides/token-optimization.md +169 -0
  69. package/hooks/lifecycle/cost-tracker.md +49 -0
  70. package/hooks/lifecycle/cost-tracker.sh +59 -0
  71. package/hooks/lifecycle/pre-compact-save.md +56 -0
  72. package/hooks/lifecycle/pre-compact-save.sh +37 -0
  73. package/hooks/lifecycle/session-start.md +50 -0
  74. package/hooks/lifecycle/session-start.sh +47 -0
  75. package/hooks/post-tool-use/audit-log.md +53 -0
  76. package/hooks/post-tool-use/audit-log.sh +53 -0
  77. package/hooks/post-tool-use/prettier.md +53 -0
  78. package/hooks/post-tool-use/prettier.sh +49 -0
  79. package/hooks/pre-tool-use/block-dangerous.md +48 -0
  80. package/hooks/pre-tool-use/block-dangerous.sh +76 -0
  81. package/hooks/pre-tool-use/git-push-confirm.md +46 -0
  82. package/hooks/pre-tool-use/git-push-confirm.sh +36 -0
  83. package/mcp/configs/github.json +11 -0
  84. package/mcp/configs/postgres.json +11 -0
  85. package/mcp/de/recommended-servers.md +170 -0
  86. package/mcp/es/recommended-servers.md +170 -0
  87. package/mcp/fr/recommended-servers.md +170 -0
  88. package/mcp/nl/recommended-servers.md +170 -0
  89. package/mcp/recommended-servers.md +168 -0
  90. package/package.json +45 -0
  91. package/prompts/project-starters/de/fastapi-project.md +62 -0
  92. package/prompts/project-starters/de/nextjs-project.md +82 -0
  93. package/prompts/project-starters/es/fastapi-project.md +62 -0
  94. package/prompts/project-starters/es/nextjs-project.md +82 -0
  95. package/prompts/project-starters/fastapi-project.md +60 -0
  96. package/prompts/project-starters/fr/fastapi-project.md +62 -0
  97. package/prompts/project-starters/fr/nextjs-project.md +82 -0
  98. package/prompts/project-starters/nextjs-project.md +80 -0
  99. package/prompts/project-starters/nl/fastapi-project.md +62 -0
  100. package/prompts/project-starters/nl/nextjs-project.md +82 -0
  101. package/prompts/system-prompts/ai-product.md +80 -0
  102. package/prompts/system-prompts/data-pipeline.md +76 -0
  103. package/prompts/system-prompts/de/ai-product.md +82 -0
  104. package/prompts/system-prompts/de/data-pipeline.md +78 -0
  105. package/prompts/system-prompts/de/saas-backend.md +71 -0
  106. package/prompts/system-prompts/es/ai-product.md +82 -0
  107. package/prompts/system-prompts/es/data-pipeline.md +78 -0
  108. package/prompts/system-prompts/es/saas-backend.md +71 -0
  109. package/prompts/system-prompts/fr/ai-product.md +82 -0
  110. package/prompts/system-prompts/fr/data-pipeline.md +78 -0
  111. package/prompts/system-prompts/fr/saas-backend.md +71 -0
  112. package/prompts/system-prompts/nl/ai-product.md +82 -0
  113. package/prompts/system-prompts/nl/data-pipeline.md +78 -0
  114. package/prompts/system-prompts/nl/saas-backend.md +71 -0
  115. package/prompts/system-prompts/saas-backend.md +69 -0
  116. package/prompts/task-specific/changelog.md +81 -0
  117. package/prompts/task-specific/de/changelog.md +83 -0
  118. package/prompts/task-specific/de/debugging.md +78 -0
  119. package/prompts/task-specific/de/pr-description.md +69 -0
  120. package/prompts/task-specific/debugging.md +76 -0
  121. package/prompts/task-specific/es/changelog.md +83 -0
  122. package/prompts/task-specific/es/debugging.md +78 -0
  123. package/prompts/task-specific/es/pr-description.md +69 -0
  124. package/prompts/task-specific/fr/changelog.md +83 -0
  125. package/prompts/task-specific/fr/debugging.md +78 -0
  126. package/prompts/task-specific/fr/pr-description.md +69 -0
  127. package/prompts/task-specific/nl/changelog.md +83 -0
  128. package/prompts/task-specific/nl/debugging.md +78 -0
  129. package/prompts/task-specific/nl/pr-description.md +69 -0
  130. package/prompts/task-specific/pr-description.md +67 -0
  131. package/rules/common/coding-style.md +45 -0
  132. package/rules/common/de/coding-style.md +47 -0
  133. package/rules/common/de/git.md +48 -0
  134. package/rules/common/de/performance.md +40 -0
  135. package/rules/common/de/security.md +45 -0
  136. package/rules/common/de/testing.md +45 -0
  137. package/rules/common/es/coding-style.md +47 -0
  138. package/rules/common/es/git.md +48 -0
  139. package/rules/common/es/performance.md +40 -0
  140. package/rules/common/es/security.md +45 -0
  141. package/rules/common/es/testing.md +45 -0
  142. package/rules/common/fr/coding-style.md +47 -0
  143. package/rules/common/fr/git.md +48 -0
  144. package/rules/common/fr/performance.md +40 -0
  145. package/rules/common/fr/security.md +45 -0
  146. package/rules/common/fr/testing.md +45 -0
  147. package/rules/common/git.md +46 -0
  148. package/rules/common/nl/coding-style.md +47 -0
  149. package/rules/common/nl/git.md +48 -0
  150. package/rules/common/nl/performance.md +40 -0
  151. package/rules/common/nl/security.md +45 -0
  152. package/rules/common/nl/testing.md +45 -0
  153. package/rules/common/performance.md +38 -0
  154. package/rules/common/security.md +43 -0
  155. package/rules/common/testing.md +43 -0
  156. package/rules/language-specific/de/go.md +48 -0
  157. package/rules/language-specific/de/python.md +38 -0
  158. package/rules/language-specific/de/typescript.md +51 -0
  159. package/rules/language-specific/es/go.md +48 -0
  160. package/rules/language-specific/es/python.md +38 -0
  161. package/rules/language-specific/es/typescript.md +51 -0
  162. package/rules/language-specific/fr/go.md +48 -0
  163. package/rules/language-specific/fr/python.md +38 -0
  164. package/rules/language-specific/fr/typescript.md +51 -0
  165. package/rules/language-specific/go.md +46 -0
  166. package/rules/language-specific/nl/go.md +48 -0
  167. package/rules/language-specific/nl/python.md +38 -0
  168. package/rules/language-specific/nl/typescript.md +51 -0
  169. package/rules/language-specific/python.md +36 -0
  170. package/rules/language-specific/typescript.md +49 -0
  171. package/scripts/cli.js +161 -0
  172. package/scripts/link-skills.sh +35 -0
  173. package/scripts/list-skills.sh +34 -0
  174. package/skills/ai-engineering/agent-construction.md +285 -0
  175. package/skills/ai-engineering/claude-api.md +248 -0
  176. package/skills/ai-engineering/de/agent-construction.md +287 -0
  177. package/skills/ai-engineering/de/claude-api.md +250 -0
  178. package/skills/ai-engineering/es/agent-construction.md +287 -0
  179. package/skills/ai-engineering/es/claude-api.md +250 -0
  180. package/skills/ai-engineering/fr/agent-construction.md +287 -0
  181. package/skills/ai-engineering/fr/claude-api.md +250 -0
  182. package/skills/ai-engineering/nl/agent-construction.md +287 -0
  183. package/skills/ai-engineering/nl/claude-api.md +250 -0
  184. package/skills/backend/dotnet/csharp.md +304 -0
  185. package/skills/backend/dotnet/de/csharp.md +306 -0
  186. package/skills/backend/dotnet/es/csharp.md +306 -0
  187. package/skills/backend/dotnet/fr/csharp.md +306 -0
  188. package/skills/backend/dotnet/nl/csharp.md +306 -0
  189. package/skills/backend/go/de/go.md +307 -0
  190. package/skills/backend/go/es/go.md +307 -0
  191. package/skills/backend/go/fr/go.md +307 -0
  192. package/skills/backend/go/go.md +305 -0
  193. package/skills/backend/go/nl/go.md +307 -0
  194. package/skills/backend/nodejs/de/nestjs.md +274 -0
  195. package/skills/backend/nodejs/de/nextjs.md +222 -0
  196. package/skills/backend/nodejs/es/nestjs.md +274 -0
  197. package/skills/backend/nodejs/es/nextjs.md +222 -0
  198. package/skills/backend/nodejs/fr/nestjs.md +274 -0
  199. package/skills/backend/nodejs/fr/nextjs.md +222 -0
  200. package/skills/backend/nodejs/nestjs.md +272 -0
  201. package/skills/backend/nodejs/nextjs.md +220 -0
  202. package/skills/backend/nodejs/nl/nestjs.md +274 -0
  203. package/skills/backend/nodejs/nl/nextjs.md +222 -0
  204. package/skills/backend/python/de/django.md +285 -0
  205. package/skills/backend/python/de/fastapi.md +244 -0
  206. package/skills/backend/python/django.md +283 -0
  207. package/skills/backend/python/es/django.md +285 -0
  208. package/skills/backend/python/es/fastapi.md +244 -0
  209. package/skills/backend/python/fastapi.md +242 -0
  210. package/skills/backend/python/fr/django.md +285 -0
  211. package/skills/backend/python/fr/fastapi.md +244 -0
  212. package/skills/backend/python/nl/django.md +285 -0
  213. package/skills/backend/python/nl/fastapi.md +244 -0
  214. package/skills/data-ml/dbt-data-pipelines.md +155 -0
  215. package/skills/data-ml/de/dbt-data-pipelines.md +157 -0
  216. package/skills/data-ml/de/pandas-polars.md +147 -0
  217. package/skills/data-ml/de/pytorch-tensorflow.md +171 -0
  218. package/skills/data-ml/es/dbt-data-pipelines.md +157 -0
  219. package/skills/data-ml/es/pandas-polars.md +147 -0
  220. package/skills/data-ml/es/pytorch-tensorflow.md +171 -0
  221. package/skills/data-ml/fr/dbt-data-pipelines.md +157 -0
  222. package/skills/data-ml/fr/pandas-polars.md +147 -0
  223. package/skills/data-ml/fr/pytorch-tensorflow.md +171 -0
  224. package/skills/data-ml/nl/dbt-data-pipelines.md +157 -0
  225. package/skills/data-ml/nl/pandas-polars.md +147 -0
  226. package/skills/data-ml/nl/pytorch-tensorflow.md +171 -0
  227. package/skills/data-ml/pandas-polars.md +145 -0
  228. package/skills/data-ml/pytorch-tensorflow.md +169 -0
  229. package/skills/database/de/graphql.md +181 -0
  230. package/skills/database/es/graphql.md +181 -0
  231. package/skills/database/fr/graphql.md +181 -0
  232. package/skills/database/graphql.md +179 -0
  233. package/skills/database/nl/graphql.md +181 -0
  234. package/skills/devops-infra/de/docker.md +133 -0
  235. package/skills/devops-infra/de/github-actions.md +179 -0
  236. package/skills/devops-infra/de/kubernetes.md +129 -0
  237. package/skills/devops-infra/de/terraform.md +130 -0
  238. package/skills/devops-infra/docker.md +131 -0
  239. package/skills/devops-infra/es/docker.md +133 -0
  240. package/skills/devops-infra/es/github-actions.md +179 -0
  241. package/skills/devops-infra/es/kubernetes.md +129 -0
  242. package/skills/devops-infra/es/terraform.md +130 -0
  243. package/skills/devops-infra/fr/docker.md +133 -0
  244. package/skills/devops-infra/fr/github-actions.md +179 -0
  245. package/skills/devops-infra/fr/kubernetes.md +129 -0
  246. package/skills/devops-infra/fr/terraform.md +130 -0
  247. package/skills/devops-infra/github-actions.md +177 -0
  248. package/skills/devops-infra/kubernetes.md +127 -0
  249. package/skills/devops-infra/nl/docker.md +133 -0
  250. package/skills/devops-infra/nl/github-actions.md +179 -0
  251. package/skills/devops-infra/nl/kubernetes.md +129 -0
  252. package/skills/devops-infra/nl/terraform.md +130 -0
  253. package/skills/devops-infra/terraform.md +128 -0
  254. package/skills/finance-payments/de/stripe.md +187 -0
  255. package/skills/finance-payments/es/stripe.md +187 -0
  256. package/skills/finance-payments/fr/stripe.md +187 -0
  257. package/skills/finance-payments/nl/stripe.md +187 -0
  258. package/skills/finance-payments/stripe.md +185 -0
  259. package/workflows/code-review.md +151 -0
  260. package/workflows/de/code-review.md +153 -0
  261. package/workflows/de/debugging-session.md +146 -0
  262. package/workflows/de/feature-development.md +155 -0
  263. package/workflows/de/new-project-bootstrap.md +175 -0
  264. package/workflows/de/refactor-safely.md +150 -0
  265. package/workflows/debugging-session.md +144 -0
  266. package/workflows/es/code-review.md +153 -0
  267. package/workflows/es/debugging-session.md +146 -0
  268. package/workflows/es/feature-development.md +155 -0
  269. package/workflows/es/new-project-bootstrap.md +175 -0
  270. package/workflows/es/refactor-safely.md +150 -0
  271. package/workflows/feature-development.md +153 -0
  272. package/workflows/fr/code-review.md +153 -0
  273. package/workflows/fr/debugging-session.md +146 -0
  274. package/workflows/fr/feature-development.md +155 -0
  275. package/workflows/fr/new-project-bootstrap.md +175 -0
  276. package/workflows/fr/refactor-safely.md +150 -0
  277. package/workflows/new-project-bootstrap.md +173 -0
  278. package/workflows/nl/code-review.md +153 -0
  279. package/workflows/nl/debugging-session.md +146 -0
  280. package/workflows/nl/feature-development.md +155 -0
  281. package/workflows/nl/new-project-bootstrap.md +175 -0
  282. package/workflows/nl/refactor-safely.md +150 -0
  283. package/workflows/refactor-safely.md +148 -0
@@ -0,0 +1,222 @@
1
+ > 🇫🇷 This is the French translation. [English version](../nextjs.md).
2
+
3
+ # Compétence Next.js
4
+
5
+ ## Quand activer
6
+ - Construire une application Next.js avec l'App Router
7
+ - Décider entre les Server Components et les Client Components
8
+ - Rédiger des Server Actions pour les soumissions de formulaires et les mutations
9
+ - Configurer des route handlers (endpoints API dans l'App Router)
10
+ - Implémenter l'authentification avec NextAuth ou un pattern JWT
11
+ - Configurer le middleware pour les redirections et les guards d'auth
12
+ - Optimiser la récupération de données avec `cache()` et `unstable_cache` de React
13
+ - Utiliser les routes parallèles, les routes d'interception ou les groupes de routes
14
+
15
+ ## Quand NE PAS utiliser
16
+ - Projets Pages Router — les patterns diffèrent significativement
17
+ - SPAs pures sans rendu serveur (utiliser Vite + React)
18
+ - Backends NestJS ou Express — utiliser la compétence NestJS
19
+ - Sites statiques sans données dynamiques (utiliser Astro)
20
+
21
+ ## Instructions
22
+
23
+ ### Structure des répertoires App Router
24
+ ```
25
+ app/
26
+ ├── (auth)/ # Groupe de routes — pas de segment URL
27
+ │ ├── login/
28
+ │ │ └── page.tsx
29
+ │ └── layout.tsx # Layout spécifique à l'auth
30
+ ├── (dashboard)/
31
+ │ ├── dashboard/
32
+ │ │ ├── page.tsx # Server Component par défaut
33
+ │ │ └── loading.tsx # UI de la boundary Suspense
34
+ │ └── layout.tsx
35
+ ├── api/
36
+ │ └── webhooks/
37
+ │ └── stripe/
38
+ │ └── route.ts # Route Handler
39
+ ├── layout.tsx # Layout racine (requis)
40
+ └── page.tsx # Page d'accueil
41
+ components/
42
+ ├── ui/ # Présentationnels (peuvent être server ou client)
43
+ └── forms/ # Toujours des client components (useState/événements)
44
+ lib/
45
+ ├── auth.ts
46
+ ├── db.ts
47
+ └── actions/ # Server Actions
48
+ └── user.ts
49
+ ```
50
+
51
+ ### Server Components vs. Client Components
52
+ ```tsx
53
+ // Server Component (défaut) — s'exécute sur le serveur, jamais envoyé au client
54
+ // Peut : await fetch, lire la DB, accéder aux variables d'environnement
55
+ // Ne peut pas : useState, useEffect, APIs navigateur, gestionnaires d'événements
56
+ export default async function UserProfile({ id }: { id: string }) {
57
+ const user = await db.user.findUnique({ where: { id } })
58
+ return <div>{user.name}</div>
59
+ }
60
+
61
+ // Client Component — ajouter la directive 'use client'
62
+ 'use client'
63
+ import { useState } from 'react'
64
+
65
+ export function LikeButton({ initialCount }: { initialCount: number }) {
66
+ const [count, setCount] = useState(initialCount)
67
+ return <button onClick={() => setCount(c => c + 1)}>{count} likes</button>
68
+ }
69
+ ```
70
+
71
+ Règle : par défaut Server Components. Ajouter `'use client'` uniquement quand vous avez besoin d'interactivité, d'APIs navigateur ou de hooks React.
72
+
73
+ ### Patterns de récupération de données
74
+ ```tsx
75
+ // Server Component — async/await direct, pas de useEffect, pas de useState
76
+ export default async function PostList() {
77
+ const posts = await db.post.findMany({ where: { published: true } })
78
+ return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
79
+ }
80
+
81
+ // Déduplication — React cache() enveloppe une fonction pour que plusieurs composants
82
+ // l'appelant dans un même rendu partagent une seule requête
83
+ import { cache } from 'react'
84
+ export const getUser = cache(async (id: string) => {
85
+ return db.user.findUnique({ where: { id } })
86
+ })
87
+
88
+ // Génération statique avec revalidation
89
+ async function getProducts() {
90
+ const res = await fetch('https://api.example.com/products', {
91
+ next: { revalidate: 3600 }, // ISR — revalider toutes les heures
92
+ })
93
+ return res.json()
94
+ }
95
+ ```
96
+
97
+ ### Server Actions
98
+ ```tsx
99
+ // lib/actions/user.ts
100
+ 'use server'
101
+ import { revalidatePath } from 'next/cache'
102
+ import { z } from 'zod'
103
+
104
+ const UpdateSchema = z.object({
105
+ name: z.string().min(1),
106
+ })
107
+
108
+ export async function updateUser(formData: FormData) {
109
+ const parsed = UpdateSchema.safeParse({ name: formData.get('name') })
110
+ if (!parsed.success) return { error: parsed.error.flatten() }
111
+
112
+ await db.user.update({ where: { id: getCurrentUserId() }, data: parsed.data })
113
+ revalidatePath('/dashboard/profile')
114
+ return { success: true }
115
+ }
116
+
117
+ // Dans un Server Component :
118
+ export default function ProfileForm({ user }: { user: User }) {
119
+ return (
120
+ <form action={updateUser}>
121
+ <input name="name" defaultValue={user.name} />
122
+ <button type="submit">Save</button>
123
+ </form>
124
+ )
125
+ }
126
+ ```
127
+
128
+ ### Route Handlers
129
+ ```ts
130
+ // app/api/webhooks/stripe/route.ts
131
+ import { NextRequest, NextResponse } from 'next/server'
132
+ import Stripe from 'stripe'
133
+
134
+ export async function POST(req: NextRequest) {
135
+ const body = await req.text()
136
+ const sig = req.headers.get('stripe-signature')!
137
+
138
+ let event: Stripe.Event
139
+ try {
140
+ event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
141
+ } catch {
142
+ return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
143
+ }
144
+
145
+ if (event.type === 'checkout.session.completed') {
146
+ await handleCheckoutCompleted(event.data.object)
147
+ }
148
+
149
+ return NextResponse.json({ received: true })
150
+ }
151
+ ```
152
+
153
+ ### Middleware
154
+ ```ts
155
+ // middleware.ts (au niveau racine)
156
+ import { NextResponse } from 'next/server'
157
+ import type { NextRequest } from 'next/server'
158
+
159
+ export function middleware(request: NextRequest) {
160
+ const token = request.cookies.get('auth-token')
161
+ if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
162
+ return NextResponse.redirect(new URL('/login', request.url))
163
+ }
164
+ return NextResponse.next()
165
+ }
166
+
167
+ export const config = {
168
+ matcher: ['/dashboard/:path*', '/api/protected/:path*'],
169
+ }
170
+ ```
171
+
172
+ ### Routes parallèles et d'interception
173
+ ```
174
+ app/
175
+ ├── @modal/ # Route parallèle — s'affiche à côté du contenu principal
176
+ │ └── (.)photo/ # Route d'interception — intercepte /photo/[id]
177
+ │ └── [id]/
178
+ │ └── page.tsx
179
+ ├── photo/
180
+ │ └── [id]/
181
+ │ └── page.tsx
182
+ └── layout.tsx # Accepte { children, modal } comme props
183
+ ```
184
+
185
+ ### Boundaries d'erreur et de chargement
186
+ ```tsx
187
+ // app/dashboard/loading.tsx — affiché pendant Suspense
188
+ export default function Loading() {
189
+ return <DashboardSkeleton />
190
+ }
191
+
192
+ // app/dashboard/error.tsx — boundary d'erreur pour ce segment
193
+ 'use client'
194
+ export default function Error({ error, reset }: { error: Error; reset: () => void }) {
195
+ return (
196
+ <div>
197
+ <p>{error.message}</p>
198
+ <button onClick={reset}>Retry</button>
199
+ </div>
200
+ )
201
+ }
202
+ ```
203
+
204
+ ### Variables d'environnement
205
+ - `NEXT_PUBLIC_*` — exposées au navigateur
206
+ - Toutes les autres — côté serveur uniquement (jamais accessibles dans les Client Components)
207
+ - Ne jamais importer une variable d'environnement server-only dans un Client Component — elle retourne `undefined` silencieusement
208
+
209
+ ## Exemple
210
+
211
+ **Utilisateur :** Ajouter une page de blog paginée à `/blog` qui récupère depuis PostgreSQL, avec une modale "Nouvel article" qui s'ouvre à `/blog/new` sans naviguer hors de la liste des articles.
212
+
213
+ **Sortie attendue :**
214
+ - `app/blog/page.tsx` — Server Component, récupère les articles avec `db.post.findMany`, affiche `<PostList>` + `<Link href="/blog/new">`
215
+ - `app/@modal/(.)blog/new/page.tsx` — Route d'interception affichant un Client Component `<NewPostModal>`
216
+ - `app/blog/new/page.tsx` — Page de repli pour la navigation directe
217
+ - `app/layout.tsx` — Mis à jour pour accepter le slot `modal` de la route parallèle et l'afficher aux côtés de `children`
218
+ - `lib/actions/post.ts` — Server Action `createPost` avec validation Zod + `revalidatePath('/blog')`
219
+
220
+ ---
221
+
222
+ > **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. [uitbreiden.com](https://uitbreiden.com/)
@@ -0,0 +1,272 @@
1
+ # NestJS Skill
2
+
3
+ ## When to activate
4
+ - Building a NestJS application (modules, controllers, services)
5
+ - Setting up guards, interceptors, pipes, and exception filters
6
+ - Integrating TypeORM or Prisma with NestJS
7
+ - Implementing CQRS with commands, queries, and events
8
+ - Setting up microservices (TCP, Redis, RabbitMQ transport)
9
+ - Writing unit and e2e tests with Jest and Supertest
10
+ - Generating OpenAPI docs with `@nestjs/swagger`
11
+
12
+ ## When NOT to use
13
+ - Next.js API routes or Fastify standalone — different framework
14
+ - Simple Express scripts — NestJS overhead isn't justified
15
+ - Lambda functions where cold start time is critical
16
+
17
+ ## Instructions
18
+
19
+ ### Module structure
20
+ ```
21
+ src/
22
+ ├── app.module.ts # Root module
23
+ ├── main.ts # Bootstrap
24
+ ├── common/
25
+ │ ├── decorators/
26
+ │ ├── filters/
27
+ │ ├── guards/
28
+ │ ├── interceptors/
29
+ │ └── pipes/
30
+ ├── config/
31
+ │ └── configuration.ts # ConfigService setup
32
+ └── modules/
33
+ └── users/
34
+ ├── users.module.ts
35
+ ├── users.controller.ts
36
+ ├── users.service.ts
37
+ ├── dto/
38
+ │ ├── create-user.dto.ts
39
+ │ └── update-user.dto.ts
40
+ ├── entities/
41
+ │ └── user.entity.ts
42
+ └── users.service.spec.ts
43
+ ```
44
+
45
+ ### Bootstrap
46
+ ```ts
47
+ // main.ts
48
+ import { NestFactory } from '@nestjs/core'
49
+ import { AppModule } from './app.module'
50
+ import { ValidationPipe } from '@nestjs/common'
51
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
52
+
53
+ async function bootstrap() {
54
+ const app = await NestFactory.create(AppModule)
55
+
56
+ app.useGlobalPipes(new ValidationPipe({
57
+ whitelist: true, // strip unknown fields
58
+ forbidNonWhitelisted: true,
59
+ transform: true, // auto-transform payloads to DTO classes
60
+ }))
61
+
62
+ const config = new DocumentBuilder()
63
+ .setTitle('API')
64
+ .setVersion('1.0')
65
+ .addBearerAuth()
66
+ .build()
67
+ SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, config))
68
+
69
+ await app.listen(3000)
70
+ }
71
+ bootstrap()
72
+ ```
73
+
74
+ ### Module, Controller, Service
75
+ ```ts
76
+ // users.module.ts
77
+ @Module({
78
+ imports: [TypeOrmModule.forFeature([User]), JwtModule.register({})],
79
+ controllers: [UsersController],
80
+ providers: [UsersService],
81
+ exports: [UsersService],
82
+ })
83
+ export class UsersModule {}
84
+
85
+ // users.controller.ts
86
+ @ApiTags('users')
87
+ @ApiBearerAuth()
88
+ @UseGuards(JwtAuthGuard)
89
+ @Controller('users')
90
+ export class UsersController {
91
+ constructor(private readonly usersService: UsersService) {}
92
+
93
+ @Post()
94
+ @HttpCode(HttpStatus.CREATED)
95
+ create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
96
+ return this.usersService.create(dto)
97
+ }
98
+
99
+ @Get(':id')
100
+ findOne(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
101
+ return this.usersService.findOneOrFail(id)
102
+ }
103
+ }
104
+
105
+ // users.service.ts
106
+ @Injectable()
107
+ export class UsersService {
108
+ constructor(
109
+ @InjectRepository(User)
110
+ private readonly userRepo: Repository<User>,
111
+ ) {}
112
+
113
+ async create(dto: CreateUserDto): Promise<User> {
114
+ const exists = await this.userRepo.findOneBy({ email: dto.email })
115
+ if (exists) throw new ConflictException('Email already in use')
116
+ const user = this.userRepo.create({
117
+ ...dto,
118
+ password: await bcrypt.hash(dto.password, 10),
119
+ })
120
+ return this.userRepo.save(user)
121
+ }
122
+
123
+ async findOneOrFail(id: string): Promise<User> {
124
+ const user = await this.userRepo.findOneBy({ id })
125
+ if (!user) throw new NotFoundException(`User ${id} not found`)
126
+ return user
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### DTOs with class-validator
132
+ ```ts
133
+ // dto/create-user.dto.ts
134
+ import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator'
135
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
136
+
137
+ export class CreateUserDto {
138
+ @ApiProperty({ example: 'user@example.com' })
139
+ @IsEmail()
140
+ email: string
141
+
142
+ @ApiProperty({ minLength: 8 })
143
+ @IsString()
144
+ @MinLength(8)
145
+ password: string
146
+
147
+ @ApiPropertyOptional()
148
+ @IsOptional()
149
+ @IsString()
150
+ name?: string
151
+ }
152
+ ```
153
+
154
+ ### Guards
155
+ ```ts
156
+ // common/guards/jwt-auth.guard.ts
157
+ @Injectable()
158
+ export class JwtAuthGuard extends AuthGuard('jwt') {
159
+ handleRequest<T>(err: Error, user: T, info: Error): T {
160
+ if (err || !user) {
161
+ throw err || new UnauthorizedException(info?.message)
162
+ }
163
+ return user
164
+ }
165
+ }
166
+
167
+ // common/guards/roles.guard.ts
168
+ @Injectable()
169
+ export class RolesGuard implements CanActivate {
170
+ constructor(private reflector: Reflector) {}
171
+
172
+ canActivate(context: ExecutionContext): boolean {
173
+ const roles = this.reflector.getAllAndOverride<Role[]>('roles', [
174
+ context.getHandler(),
175
+ context.getClass(),
176
+ ])
177
+ if (!roles) return true
178
+ const { user } = context.switchToHttp().getRequest()
179
+ return roles.some(role => user.roles?.includes(role))
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Interceptors
185
+ ```ts
186
+ // common/interceptors/transform.interceptor.ts
187
+ @Injectable()
188
+ export class TransformInterceptor<T>
189
+ implements NestInterceptor<T, { data: T; timestamp: string }>
190
+ {
191
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
192
+ return next.handle().pipe(
193
+ map(data => ({ data, timestamp: new Date().toISOString() }))
194
+ )
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### CQRS
200
+ ```ts
201
+ // Install: @nestjs/cqrs
202
+
203
+ // commands/create-user.command.ts
204
+ export class CreateUserCommand {
205
+ constructor(public readonly dto: CreateUserDto) {}
206
+ }
207
+
208
+ // commands/create-user.handler.ts
209
+ @CommandHandler(CreateUserCommand)
210
+ export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
211
+ constructor(private readonly userRepo: UserRepository) {}
212
+
213
+ async execute(command: CreateUserCommand): Promise<User> {
214
+ const user = User.create(command.dto)
215
+ await this.userRepo.save(user)
216
+ return user
217
+ }
218
+ }
219
+
220
+ // In service/controller:
221
+ const user = await this.commandBus.execute(new CreateUserCommand(dto))
222
+ ```
223
+
224
+ ### Testing
225
+ ```ts
226
+ // Unit test
227
+ describe('UsersService', () => {
228
+ let service: UsersService
229
+ let repo: jest.Mocked<Repository<User>>
230
+
231
+ beforeEach(async () => {
232
+ const module = await Test.createTestingModule({
233
+ providers: [
234
+ UsersService,
235
+ { provide: getRepositoryToken(User), useValue: createMockRepository() },
236
+ ],
237
+ }).compile()
238
+ service = module.get(UsersService)
239
+ repo = module.get(getRepositoryToken(User))
240
+ })
241
+
242
+ it('throws ConflictException for duplicate email', async () => {
243
+ repo.findOneBy.mockResolvedValue(existingUser)
244
+ await expect(service.create(dto)).rejects.toThrow(ConflictException)
245
+ })
246
+ })
247
+
248
+ // E2E test
249
+ describe('POST /users', () => {
250
+ it('creates a user', () => {
251
+ return request(app.getHttpServer())
252
+ .post('/users')
253
+ .send({ email: 'a@b.com', password: 'password123' })
254
+ .expect(201)
255
+ })
256
+ })
257
+ ```
258
+
259
+ ## Example
260
+
261
+ **User:** Add a `Products` module to a NestJS app with TypeORM, including CRUD endpoints, admin-only delete protected by a `RolesGuard`, and OpenAPI docs.
262
+
263
+ **Expected output:**
264
+ - `products.entity.ts` — TypeORM entity with `id` (UUID), `name`, `price` (decimal), `stock`, `createdAt`
265
+ - `dto/create-product.dto.ts` — class-validator DTO with `@ApiProperty` decorators
266
+ - `products.service.ts` — CRUD methods using `Repository<Product>`, throwing `NotFoundException` on missing
267
+ - `products.controller.ts` — all CRUD endpoints, `@UseGuards(JwtAuthGuard, RolesGuard)` + `@Roles(Role.Admin)` on `DELETE`
268
+ - `products.module.ts` — imports `TypeOrmModule.forFeature([Product])`
269
+
270
+ ---
271
+
272
+ > **Work with us:** Claudient is backed by [Uitbreiden](https://uitbreiden.com/) — we build AI products and B2B solutions with developer communities. [uitbreiden.com](https://uitbreiden.com/)