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,220 @@
1
+ # Next.js Skill
2
+
3
+ ## When to activate
4
+ - Building a Next.js application using the App Router
5
+ - Deciding between Server Components and Client Components
6
+ - Writing Server Actions for form submissions and mutations
7
+ - Setting up route handlers (API endpoints in App Router)
8
+ - Implementing authentication with NextAuth or a JWT pattern
9
+ - Configuring middleware for redirects and auth guards
10
+ - Optimizing data fetching with React `cache()` and `unstable_cache`
11
+ - Using parallel routes, intercepting routes, or route groups
12
+
13
+ ## When NOT to use
14
+ - Pages Router projects — the patterns differ significantly
15
+ - Pure SPAs with no server rendering (use Vite + React)
16
+ - NestJS or Express backends — use NestJS skill
17
+ - Static sites with no dynamic data (use Astro)
18
+
19
+ ## Instructions
20
+
21
+ ### App Router directory structure
22
+ ```
23
+ app/
24
+ ├── (auth)/ # Route group — no URL segment
25
+ │ ├── login/
26
+ │ │ └── page.tsx
27
+ │ └── layout.tsx # Auth-specific layout
28
+ ├── (dashboard)/
29
+ │ ├── dashboard/
30
+ │ │ ├── page.tsx # Server Component by default
31
+ │ │ └── loading.tsx # Suspense boundary UI
32
+ │ └── layout.tsx
33
+ ├── api/
34
+ │ └── webhooks/
35
+ │ └── stripe/
36
+ │ └── route.ts # Route Handler
37
+ ├── layout.tsx # Root layout (required)
38
+ └── page.tsx # Home page
39
+ components/
40
+ ├── ui/ # Presentational (can be server or client)
41
+ └── forms/ # Always client components (useState/events)
42
+ lib/
43
+ ├── auth.ts
44
+ ├── db.ts
45
+ └── actions/ # Server Actions
46
+ └── user.ts
47
+ ```
48
+
49
+ ### Server vs. Client Components
50
+ ```tsx
51
+ // Server Component (default) — runs on server, never sent to client
52
+ // Can: await fetch, read DB, access env vars
53
+ // Cannot: useState, useEffect, browser APIs, event handlers
54
+ export default async function UserProfile({ id }: { id: string }) {
55
+ const user = await db.user.findUnique({ where: { id } })
56
+ return <div>{user.name}</div>
57
+ }
58
+
59
+ // Client Component — add 'use client' directive
60
+ 'use client'
61
+ import { useState } from 'react'
62
+
63
+ export function LikeButton({ initialCount }: { initialCount: number }) {
64
+ const [count, setCount] = useState(initialCount)
65
+ return <button onClick={() => setCount(c => c + 1)}>{count} likes</button>
66
+ }
67
+ ```
68
+
69
+ Rule: default to Server Components. Add `'use client'` only when you need interactivity, browser APIs, or React hooks.
70
+
71
+ ### Data fetching patterns
72
+ ```tsx
73
+ // Server Component — direct async/await, no useEffect, no useState
74
+ export default async function PostList() {
75
+ const posts = await db.post.findMany({ where: { published: true } })
76
+ return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
77
+ }
78
+
79
+ // Deduplication — React cache() wraps a function so multiple components
80
+ // calling it in one render share one fetch
81
+ import { cache } from 'react'
82
+ export const getUser = cache(async (id: string) => {
83
+ return db.user.findUnique({ where: { id } })
84
+ })
85
+
86
+ // Static generation with revalidation
87
+ async function getProducts() {
88
+ const res = await fetch('https://api.example.com/products', {
89
+ next: { revalidate: 3600 }, // ISR — revalidate every hour
90
+ })
91
+ return res.json()
92
+ }
93
+ ```
94
+
95
+ ### Server Actions
96
+ ```tsx
97
+ // lib/actions/user.ts
98
+ 'use server'
99
+ import { revalidatePath } from 'next/cache'
100
+ import { z } from 'zod'
101
+
102
+ const UpdateSchema = z.object({
103
+ name: z.string().min(1),
104
+ })
105
+
106
+ export async function updateUser(formData: FormData) {
107
+ const parsed = UpdateSchema.safeParse({ name: formData.get('name') })
108
+ if (!parsed.success) return { error: parsed.error.flatten() }
109
+
110
+ await db.user.update({ where: { id: getCurrentUserId() }, data: parsed.data })
111
+ revalidatePath('/dashboard/profile')
112
+ return { success: true }
113
+ }
114
+
115
+ // In a Server Component:
116
+ export default function ProfileForm({ user }: { user: User }) {
117
+ return (
118
+ <form action={updateUser}>
119
+ <input name="name" defaultValue={user.name} />
120
+ <button type="submit">Save</button>
121
+ </form>
122
+ )
123
+ }
124
+ ```
125
+
126
+ ### Route Handlers
127
+ ```ts
128
+ // app/api/webhooks/stripe/route.ts
129
+ import { NextRequest, NextResponse } from 'next/server'
130
+ import Stripe from 'stripe'
131
+
132
+ export async function POST(req: NextRequest) {
133
+ const body = await req.text()
134
+ const sig = req.headers.get('stripe-signature')!
135
+
136
+ let event: Stripe.Event
137
+ try {
138
+ event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
139
+ } catch {
140
+ return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
141
+ }
142
+
143
+ if (event.type === 'checkout.session.completed') {
144
+ await handleCheckoutCompleted(event.data.object)
145
+ }
146
+
147
+ return NextResponse.json({ received: true })
148
+ }
149
+ ```
150
+
151
+ ### Middleware
152
+ ```ts
153
+ // middleware.ts (root level)
154
+ import { NextResponse } from 'next/server'
155
+ import type { NextRequest } from 'next/server'
156
+
157
+ export function middleware(request: NextRequest) {
158
+ const token = request.cookies.get('auth-token')
159
+ if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
160
+ return NextResponse.redirect(new URL('/login', request.url))
161
+ }
162
+ return NextResponse.next()
163
+ }
164
+
165
+ export const config = {
166
+ matcher: ['/dashboard/:path*', '/api/protected/:path*'],
167
+ }
168
+ ```
169
+
170
+ ### Parallel and intercepting routes
171
+ ```
172
+ app/
173
+ ├── @modal/ # Parallel route — renders alongside main content
174
+ │ └── (.)photo/ # Intercepting route — intercepts /photo/[id]
175
+ │ └── [id]/
176
+ │ └── page.tsx
177
+ ├── photo/
178
+ │ └── [id]/
179
+ │ └── page.tsx
180
+ └── layout.tsx # Accepts { children, modal } as props
181
+ ```
182
+
183
+ ### Error and loading boundaries
184
+ ```tsx
185
+ // app/dashboard/loading.tsx — shown during Suspense
186
+ export default function Loading() {
187
+ return <DashboardSkeleton />
188
+ }
189
+
190
+ // app/dashboard/error.tsx — error boundary for this segment
191
+ 'use client'
192
+ export default function Error({ error, reset }: { error: Error; reset: () => void }) {
193
+ return (
194
+ <div>
195
+ <p>{error.message}</p>
196
+ <button onClick={reset}>Retry</button>
197
+ </div>
198
+ )
199
+ }
200
+ ```
201
+
202
+ ### Environment variables
203
+ - `NEXT_PUBLIC_*` — exposed to the browser
204
+ - All others — server-only (never accessible in Client Components)
205
+ - Never import a server-only env var inside a Client Component — it returns `undefined` silently
206
+
207
+ ## Example
208
+
209
+ **User:** Add a paginated blog posts page at `/blog` that fetches from PostgreSQL, with a "New Post" modal that opens at `/blog/new` but doesn't navigate away from the posts list.
210
+
211
+ **Expected output:**
212
+ - `app/blog/page.tsx` — Server Component, fetches posts with `db.post.findMany`, renders `<PostList>` + `<Link href="/blog/new">`
213
+ - `app/@modal/(.)blog/new/page.tsx` — Intercepting route showing a `<NewPostModal>` Client Component
214
+ - `app/blog/new/page.tsx` — Full-page fallback for direct navigation
215
+ - `app/layout.tsx` — Updated to accept `modal` parallel route slot and render it alongside `children`
216
+ - `lib/actions/post.ts` — `createPost` Server Action with Zod validation + `revalidatePath('/blog')`
217
+
218
+ ---
219
+
220
+ > **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/)
@@ -0,0 +1,274 @@
1
+ > 🇳🇱 Dit is de Nederlandse vertaling. [Engelse versie](../nestjs.md).
2
+
3
+ # NestJS Skill
4
+
5
+ ## Wanneer te activeren
6
+ - Een NestJS-applicatie bouwen (modules, controllers, services)
7
+ - Guards, interceptors, pipes en exception filters instellen
8
+ - TypeORM of Prisma integreren met NestJS
9
+ - CQRS implementeren met commando's, queries en events
10
+ - Microservices instellen (TCP, Redis, RabbitMQ transport)
11
+ - Unit- en e2e-tests schrijven met Jest en Supertest
12
+ - OpenAPI-documentatie genereren met `@nestjs/swagger`
13
+
14
+ ## Wanneer NIET te gebruiken
15
+ - Next.js API-routes of standalone Fastify — ander framework
16
+ - Eenvoudige Express-scripts — NestJS-overhead is niet gerechtvaardigd
17
+ - Lambda-functies waarbij cold start-tijd kritiek is
18
+
19
+ ## Instructies
20
+
21
+ ### Module-structuur
22
+ ```
23
+ src/
24
+ ├── app.module.ts # Root module
25
+ ├── main.ts # Bootstrap
26
+ ├── common/
27
+ │ ├── decorators/
28
+ │ ├── filters/
29
+ │ ├── guards/
30
+ │ ├── interceptors/
31
+ │ └── pipes/
32
+ ├── config/
33
+ │ └── configuration.ts # ConfigService-instelling
34
+ └── modules/
35
+ └── users/
36
+ ├── users.module.ts
37
+ ├── users.controller.ts
38
+ ├── users.service.ts
39
+ ├── dto/
40
+ │ ├── create-user.dto.ts
41
+ │ └── update-user.dto.ts
42
+ ├── entities/
43
+ │ └── user.entity.ts
44
+ └── users.service.spec.ts
45
+ ```
46
+
47
+ ### Bootstrap
48
+ ```ts
49
+ // main.ts
50
+ import { NestFactory } from '@nestjs/core'
51
+ import { AppModule } from './app.module'
52
+ import { ValidationPipe } from '@nestjs/common'
53
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
54
+
55
+ async function bootstrap() {
56
+ const app = await NestFactory.create(AppModule)
57
+
58
+ app.useGlobalPipes(new ValidationPipe({
59
+ whitelist: true, // verwijder onbekende velden
60
+ forbidNonWhitelisted: true,
61
+ transform: true, // transformeer payloads automatisch naar DTO-klassen
62
+ }))
63
+
64
+ const config = new DocumentBuilder()
65
+ .setTitle('API')
66
+ .setVersion('1.0')
67
+ .addBearerAuth()
68
+ .build()
69
+ SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, config))
70
+
71
+ await app.listen(3000)
72
+ }
73
+ bootstrap()
74
+ ```
75
+
76
+ ### Module, Controller, Service
77
+ ```ts
78
+ // users.module.ts
79
+ @Module({
80
+ imports: [TypeOrmModule.forFeature([User]), JwtModule.register({})],
81
+ controllers: [UsersController],
82
+ providers: [UsersService],
83
+ exports: [UsersService],
84
+ })
85
+ export class UsersModule {}
86
+
87
+ // users.controller.ts
88
+ @ApiTags('users')
89
+ @ApiBearerAuth()
90
+ @UseGuards(JwtAuthGuard)
91
+ @Controller('users')
92
+ export class UsersController {
93
+ constructor(private readonly usersService: UsersService) {}
94
+
95
+ @Post()
96
+ @HttpCode(HttpStatus.CREATED)
97
+ create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
98
+ return this.usersService.create(dto)
99
+ }
100
+
101
+ @Get(':id')
102
+ findOne(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
103
+ return this.usersService.findOneOrFail(id)
104
+ }
105
+ }
106
+
107
+ // users.service.ts
108
+ @Injectable()
109
+ export class UsersService {
110
+ constructor(
111
+ @InjectRepository(User)
112
+ private readonly userRepo: Repository<User>,
113
+ ) {}
114
+
115
+ async create(dto: CreateUserDto): Promise<User> {
116
+ const exists = await this.userRepo.findOneBy({ email: dto.email })
117
+ if (exists) throw new ConflictException('Email already in use')
118
+ const user = this.userRepo.create({
119
+ ...dto,
120
+ password: await bcrypt.hash(dto.password, 10),
121
+ })
122
+ return this.userRepo.save(user)
123
+ }
124
+
125
+ async findOneOrFail(id: string): Promise<User> {
126
+ const user = await this.userRepo.findOneBy({ id })
127
+ if (!user) throw new NotFoundException(`User ${id} not found`)
128
+ return user
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### DTO's met class-validator
134
+ ```ts
135
+ // dto/create-user.dto.ts
136
+ import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator'
137
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
138
+
139
+ export class CreateUserDto {
140
+ @ApiProperty({ example: 'user@example.com' })
141
+ @IsEmail()
142
+ email: string
143
+
144
+ @ApiProperty({ minLength: 8 })
145
+ @IsString()
146
+ @MinLength(8)
147
+ password: string
148
+
149
+ @ApiPropertyOptional()
150
+ @IsOptional()
151
+ @IsString()
152
+ name?: string
153
+ }
154
+ ```
155
+
156
+ ### Guards
157
+ ```ts
158
+ // common/guards/jwt-auth.guard.ts
159
+ @Injectable()
160
+ export class JwtAuthGuard extends AuthGuard('jwt') {
161
+ handleRequest<T>(err: Error, user: T, info: Error): T {
162
+ if (err || !user) {
163
+ throw err || new UnauthorizedException(info?.message)
164
+ }
165
+ return user
166
+ }
167
+ }
168
+
169
+ // common/guards/roles.guard.ts
170
+ @Injectable()
171
+ export class RolesGuard implements CanActivate {
172
+ constructor(private reflector: Reflector) {}
173
+
174
+ canActivate(context: ExecutionContext): boolean {
175
+ const roles = this.reflector.getAllAndOverride<Role[]>('roles', [
176
+ context.getHandler(),
177
+ context.getClass(),
178
+ ])
179
+ if (!roles) return true
180
+ const { user } = context.switchToHttp().getRequest()
181
+ return roles.some(role => user.roles?.includes(role))
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### Interceptors
187
+ ```ts
188
+ // common/interceptors/transform.interceptor.ts
189
+ @Injectable()
190
+ export class TransformInterceptor<T>
191
+ implements NestInterceptor<T, { data: T; timestamp: string }>
192
+ {
193
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
194
+ return next.handle().pipe(
195
+ map(data => ({ data, timestamp: new Date().toISOString() }))
196
+ )
197
+ }
198
+ }
199
+ ```
200
+
201
+ ### CQRS
202
+ ```ts
203
+ // Installeer: @nestjs/cqrs
204
+
205
+ // commands/create-user.command.ts
206
+ export class CreateUserCommand {
207
+ constructor(public readonly dto: CreateUserDto) {}
208
+ }
209
+
210
+ // commands/create-user.handler.ts
211
+ @CommandHandler(CreateUserCommand)
212
+ export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
213
+ constructor(private readonly userRepo: UserRepository) {}
214
+
215
+ async execute(command: CreateUserCommand): Promise<User> {
216
+ const user = User.create(command.dto)
217
+ await this.userRepo.save(user)
218
+ return user
219
+ }
220
+ }
221
+
222
+ // In service/controller:
223
+ const user = await this.commandBus.execute(new CreateUserCommand(dto))
224
+ ```
225
+
226
+ ### Testen
227
+ ```ts
228
+ // Unit test
229
+ describe('UsersService', () => {
230
+ let service: UsersService
231
+ let repo: jest.Mocked<Repository<User>>
232
+
233
+ beforeEach(async () => {
234
+ const module = await Test.createTestingModule({
235
+ providers: [
236
+ UsersService,
237
+ { provide: getRepositoryToken(User), useValue: createMockRepository() },
238
+ ],
239
+ }).compile()
240
+ service = module.get(UsersService)
241
+ repo = module.get(getRepositoryToken(User))
242
+ })
243
+
244
+ it('throws ConflictException for duplicate email', async () => {
245
+ repo.findOneBy.mockResolvedValue(existingUser)
246
+ await expect(service.create(dto)).rejects.toThrow(ConflictException)
247
+ })
248
+ })
249
+
250
+ // E2E-test
251
+ describe('POST /users', () => {
252
+ it('creates a user', () => {
253
+ return request(app.getHttpServer())
254
+ .post('/users')
255
+ .send({ email: 'a@b.com', password: 'password123' })
256
+ .expect(201)
257
+ })
258
+ })
259
+ ```
260
+
261
+ ## Voorbeeld
262
+
263
+ **Gebruiker:** Voeg een `Products`-module toe aan een NestJS-app met TypeORM, inclusief CRUD-endpoints, admin-only delete beveiligd door een `RolesGuard`, en OpenAPI-documentatie.
264
+
265
+ **Verwachte output:**
266
+ - `products.entity.ts` — TypeORM-entiteit met `id` (UUID), `name`, `price` (decimal), `stock`, `createdAt`
267
+ - `dto/create-product.dto.ts` — class-validator DTO met `@ApiProperty`-decorators
268
+ - `products.service.ts` — CRUD-methoden met `Repository<Product>`, die `NotFoundException` gooit bij ontbrekende items
269
+ - `products.controller.ts` — alle CRUD-endpoints, `@UseGuards(JwtAuthGuard, RolesGuard)` + `@Roles(Role.Admin)` op `DELETE`
270
+ - `products.module.ts` — importeert `TypeOrmModule.forFeature([Product])`
271
+
272
+ ---
273
+
274
+ > **Werk met ons:** Claudient wordt ondersteund door [Uitbreiden](https://uitbreiden.com/) — we bouwen AI-producten en B2B-oplossingen met ontwikkelaarsgemeenschappen. [uitbreiden.com](https://uitbreiden.com/)