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,307 @@
1
+ > 🇳🇱 Dit is de Nederlandse vertaling. [Engelse versie](../go.md).
2
+
3
+ # Go Skill
4
+
5
+ ## Wanneer te activeren
6
+ - Een Go HTTP-service of CLI-tool bouwen
7
+ - Een Go-project structureren met `cmd/`, `internal/`, `pkg/`
8
+ - HTTP-handlers schrijven met `net/http`, chi of gin
9
+ - Interfaces, embedding en compositiepatronen implementeren
10
+ - Gelijktijdige code schrijven met goroutines, kanalen en `sync`-primitieven
11
+ - Foutafhandeling, inpakking en sentinaelfouten
12
+ - Tabelgestuurde tests schrijven
13
+
14
+ ## Wanneer NIET te gebruiken
15
+ - Node.js-, Python- of andere taalservices — andere ecosystemen
16
+ - Protobuf/gRPC-services zonder voorafgaande gRPC-context
17
+ - Pure scripttaken — shell of Python passen beter
18
+
19
+ ## Instructies
20
+
21
+ ### Projectindeling
22
+ ```
23
+ myapp/
24
+ ├── cmd/
25
+ │ └── server/
26
+ │ └── main.go # Ingangspunt — slank, bedraadt en start alleen
27
+ ├── internal/ # Private packages — niet van buitenaf importeerbaar
28
+ │ ├── config/
29
+ │ ├── handler/
30
+ │ ├── service/
31
+ │ └── store/
32
+ ├── pkg/ # Publieke packages — importeerbaar door andere modules
33
+ │ └── apierr/
34
+ ├── go.mod
35
+ └── go.sum
36
+ ```
37
+
38
+ ### main.go — alleen bedrading
39
+ ```go
40
+ // cmd/server/main.go
41
+ package main
42
+
43
+ import (
44
+ "context"
45
+ "log/slog"
46
+ "net/http"
47
+ "os"
48
+ "os/signal"
49
+ "syscall"
50
+ "time"
51
+
52
+ "myapp/internal/config"
53
+ "myapp/internal/handler"
54
+ "myapp/internal/store"
55
+ )
56
+
57
+ func main() {
58
+ cfg := config.MustLoad()
59
+ db := store.MustConnect(cfg.DatabaseURL)
60
+ defer db.Close()
61
+
62
+ mux := handler.NewMux(db, cfg)
63
+
64
+ srv := &http.Server{
65
+ Addr: ":" + cfg.Port,
66
+ Handler: mux,
67
+ ReadTimeout: 10 * time.Second,
68
+ WriteTimeout: 30 * time.Second,
69
+ IdleTimeout: 60 * time.Second,
70
+ }
71
+
72
+ go func() {
73
+ slog.Info("server starting", "addr", srv.Addr)
74
+ if err := srv.ListenAndServe(); err != http.ErrServerClosed {
75
+ slog.Error("server error", "err", err)
76
+ os.Exit(1)
77
+ }
78
+ }()
79
+
80
+ quit := make(chan os.Signal, 1)
81
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
82
+ <-quit
83
+
84
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
85
+ defer cancel()
86
+ srv.Shutdown(ctx)
87
+ }
88
+ ```
89
+
90
+ ### Foutafhandeling
91
+ ```go
92
+ // Definieer sentinaelfouten voor verwachte omstandigheden
93
+ var (
94
+ ErrNotFound = errors.New("not found")
95
+ ErrDuplicate = errors.New("duplicate entry")
96
+ ErrForbidden = errors.New("forbidden")
97
+ )
98
+
99
+ // Wikkel fouten in met context — gebruik %w om uitpakken te behouden
100
+ func (s *UserStore) GetByID(ctx context.Context, id string) (*User, error) {
101
+ var u User
102
+ err := s.db.QueryRowContext(ctx, `SELECT id, email FROM users WHERE id = $1`, id).
103
+ Scan(&u.ID, &u.Email)
104
+ if errors.Is(err, sql.ErrNoRows) {
105
+ return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
106
+ }
107
+ if err != nil {
108
+ return nil, fmt.Errorf("user store get: %w", err)
109
+ }
110
+ return &u, nil
111
+ }
112
+
113
+ // Aanroeper controleert gedrag, niet berichtstrings
114
+ user, err := store.GetByID(ctx, id)
115
+ if errors.Is(err, ErrNotFound) {
116
+ http.Error(w, "not found", http.StatusNotFound)
117
+ return
118
+ }
119
+ ```
120
+
121
+ ### HTTP-handlers met chi
122
+ ```go
123
+ // internal/handler/users.go
124
+ type UserHandler struct {
125
+ svc UserService
126
+ }
127
+
128
+ func (h *UserHandler) Register(r chi.Router) {
129
+ r.Get("/users/{id}", h.GetUser)
130
+ r.Post("/users", h.CreateUser)
131
+ }
132
+
133
+ func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
134
+ id := chi.URLParam(r, "id")
135
+ user, err := h.svc.GetUser(r.Context(), id)
136
+ if err != nil {
137
+ renderError(w, err)
138
+ return
139
+ }
140
+ renderJSON(w, http.StatusOK, user)
141
+ }
142
+
143
+ func renderJSON(w http.ResponseWriter, code int, v any) {
144
+ w.Header().Set("Content-Type", "application/json")
145
+ w.WriteHeader(code)
146
+ json.NewEncoder(w).Encode(v)
147
+ }
148
+
149
+ func renderError(w http.ResponseWriter, err error) {
150
+ switch {
151
+ case errors.Is(err, ErrNotFound):
152
+ http.Error(w, "not found", http.StatusNotFound)
153
+ case errors.Is(err, ErrForbidden):
154
+ http.Error(w, "forbidden", http.StatusForbidden)
155
+ default:
156
+ slog.Error("internal error", "err", err)
157
+ http.Error(w, "internal server error", http.StatusInternalServerError)
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Interfaces
163
+ ```go
164
+ // Definieer interfaces waar ze worden gebruikt (consumerkant), niet waar geïmplementeerd
165
+ // internal/handler/users.go
166
+ type UserService interface {
167
+ GetUser(ctx context.Context, id string) (*User, error)
168
+ CreateUser(ctx context.Context, req CreateUserRequest) (*User, error)
169
+ }
170
+
171
+ // Houd interfaces klein — 1-3 methoden voorkeur
172
+ // io.Reader, io.Writer zijn de canonieke voorbeelden
173
+
174
+ // Embedding voor compositie
175
+ type ReadWriter interface {
176
+ io.Reader
177
+ io.Writer
178
+ }
179
+ ```
180
+
181
+ ### Gelijktijdigheidspatronen
182
+ ```go
183
+ // Fan-out met errgroup
184
+ import "golang.org/x/sync/errgroup"
185
+
186
+ func fetchAll(ctx context.Context, ids []string) ([]*User, error) {
187
+ g, ctx := errgroup.WithContext(ctx)
188
+ results := make([]*User, len(ids))
189
+
190
+ for i, id := range ids {
191
+ i, id := i, id // vang loopvariabelen
192
+ g.Go(func() error {
193
+ u, err := store.GetByID(ctx, id)
194
+ if err != nil {
195
+ return err
196
+ }
197
+ results[i] = u
198
+ return nil
199
+ })
200
+ }
201
+
202
+ if err := g.Wait(); err != nil {
203
+ return nil, err
204
+ }
205
+ return results, nil
206
+ }
207
+
208
+ // Worker-pool
209
+ func processJobs(ctx context.Context, jobs <-chan Job) {
210
+ const workers = 10
211
+ var wg sync.WaitGroup
212
+ for range workers {
213
+ wg.Add(1)
214
+ go func() {
215
+ defer wg.Done()
216
+ for job := range jobs {
217
+ if err := process(ctx, job); err != nil {
218
+ slog.Error("job failed", "id", job.ID, "err", err)
219
+ }
220
+ }
221
+ }()
222
+ }
223
+ wg.Wait()
224
+ }
225
+ ```
226
+
227
+ ### Contextpropagatie
228
+ ```go
229
+ // Geef context altijd door als eerste argument
230
+ func (s *Service) DoWork(ctx context.Context, id string) error {
231
+ // Geef ctx door aan alle downstream-aanroepen
232
+ user, err := s.store.GetByID(ctx, id)
233
+ if err != nil {
234
+ return err
235
+ }
236
+ return s.notifier.Send(ctx, user.Email)
237
+ }
238
+
239
+ // Sla aanvraag-scoped waarden op in context alleen voor cross-cutting concerns
240
+ // (trace-ID's, auth-info) — nooit voor optionele functieparameters
241
+ type contextKey string
242
+ const requestIDKey contextKey = "request_id"
243
+
244
+ func WithRequestID(ctx context.Context, id string) context.Context {
245
+ return context.WithValue(ctx, requestIDKey, id)
246
+ }
247
+ func GetRequestID(ctx context.Context) string {
248
+ id, _ := ctx.Value(requestIDKey).(string)
249
+ return id
250
+ }
251
+ ```
252
+
253
+ ### Tabelgestuurde tests
254
+ ```go
255
+ func TestGetUser(t *testing.T) {
256
+ tests := []struct {
257
+ name string
258
+ id string
259
+ want *User
260
+ wantErr error
261
+ }{
262
+ {"found", "123", &User{ID: "123", Email: "a@b.com"}, nil},
263
+ {"not found", "999", nil, ErrNotFound},
264
+ {"empty id", "", nil, ErrNotFound},
265
+ }
266
+
267
+ for _, tt := range tests {
268
+ t.Run(tt.name, func(t *testing.T) {
269
+ svc := newTestService(t)
270
+ got, err := svc.GetUser(context.Background(), tt.id)
271
+ if !errors.Is(err, tt.wantErr) {
272
+ t.Fatalf("err = %v, want %v", err, tt.wantErr)
273
+ }
274
+ if diff := cmp.Diff(tt.want, got); diff != "" {
275
+ t.Errorf("mismatch (-want +got):\n%s", diff)
276
+ }
277
+ })
278
+ }
279
+ }
280
+ ```
281
+
282
+ ### Loggen met slog
283
+ ```go
284
+ // Gestructureerde logging — nooit fmt.Println in productiecode
285
+ slog.Info("request completed", "method", r.Method, "path", r.URL.Path, "status", 200, "duration_ms", elapsed.Milliseconds())
286
+ slog.Error("db query failed", "query", "getUserByID", "err", err)
287
+
288
+ // JSON-logger instellen in main
289
+ logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
290
+ Level: slog.LevelInfo,
291
+ }))
292
+ slog.SetDefault(logger)
293
+ ```
294
+
295
+ ## Voorbeeld
296
+
297
+ **Gebruiker:** Bouw een Go HTTP API voor een takenlijst: aanmaken, ophalen en voltooien van taken. PostgreSQL-backend, chi-router, gestructureerde logging, gracieuze afsluiting.
298
+
299
+ **Verwachte output:**
300
+ - `cmd/server/main.go` — bedraadt store, handler, start server met gracieuze afsluiting
301
+ - `internal/store/task.go` — `TaskStore` met `Create`-, `List`-, `Complete`-methoden via `database/sql`
302
+ - `internal/handler/tasks.go` — `TaskHandler` met `Register(r chi.Router)`, JSON-antwoorden
303
+ - `internal/handler/tasks_test.go` — tabelgestuurde tests met een mock-store
304
+
305
+ ---
306
+
307
+ > **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/)
@@ -0,0 +1,274 @@
1
+ > 🇩🇪 Dies ist die deutsche Übersetzung. [Englische Version](../nestjs.md).
2
+
3
+ # NestJS Skill
4
+
5
+ ## Wann aktivieren
6
+ - Eine NestJS-Anwendung bauen (Module, Controller, Services)
7
+ - Guards, Interceptors, Pipes und Exception Filter einrichten
8
+ - TypeORM oder Prisma mit NestJS integrieren
9
+ - CQRS mit Commands, Queries und Events implementieren
10
+ - Microservices einrichten (TCP-, Redis-, RabbitMQ-Transport)
11
+ - Unit- und E2E-Tests mit Jest und Supertest schreiben
12
+ - OpenAPI-Docs mit `@nestjs/swagger` generieren
13
+
14
+ ## Wann NICHT verwenden
15
+ - Next.js API-Routes oder eigenständiges Fastify — anderes Framework
16
+ - Einfache Express-Skripte — NestJS-Overhead nicht gerechtfertigt
17
+ - Lambda-Funktionen, bei denen die Kaltstart-Zeit kritisch ist
18
+
19
+ ## Anweisungen
20
+
21
+ ### Modulstruktur
22
+ ```
23
+ src/
24
+ ├── app.module.ts # Root-Modul
25
+ ├── main.ts # Bootstrap
26
+ ├── common/
27
+ │ ├── decorators/
28
+ │ ├── filters/
29
+ │ ├── guards/
30
+ │ ├── interceptors/
31
+ │ └── pipes/
32
+ ├── config/
33
+ │ └── configuration.ts # ConfigService-Setup
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, // unbekannte Felder entfernen
60
+ forbidNonWhitelisted: true,
61
+ transform: true, // Payloads automatisch in DTO-Klassen umwandeln
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
+ ### Modul, 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
+ ### DTOs mit 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
+ // Installation: @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
+ // Im Service/Controller:
223
+ const user = await this.commandBus.execute(new CreateUserCommand(dto))
224
+ ```
225
+
226
+ ### Tests
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
+ ## Beispiel
262
+
263
+ **Benutzer:** Ein `Products`-Modul zu einer NestJS-App mit TypeORM hinzufügen, einschließlich CRUD-Endpunkte, nur-Admin-Löschen durch einen `RolesGuard` geschützt, und OpenAPI-Docs.
264
+
265
+ **Erwartete Ausgabe:**
266
+ - `products.entity.ts` — TypeORM-Entity mit `id` (UUID), `name`, `price` (decimal), `stock`, `createdAt`
267
+ - `dto/create-product.dto.ts` — class-validator DTO mit `@ApiProperty`-Dekoratoren
268
+ - `products.service.ts` — CRUD-Methoden mit `Repository<Product>`, `NotFoundException` bei fehlendem Element
269
+ - `products.controller.ts` — alle CRUD-Endpunkte, `@UseGuards(JwtAuthGuard, RolesGuard)` + `@Roles(Role.Admin)` bei `DELETE`
270
+ - `products.module.ts` — importiert `TypeOrmModule.forFeature([Product])`
271
+
272
+ ---
273
+
274
+ > **Mit uns arbeiten:** Claudient wird von [Uitbreiden](https://uitbreiden.com/) unterstützt — wir bauen KI-Produkte und B2B-Lösungen mit Entwickler-Communities. [uitbreiden.com](https://uitbreiden.com/)