@devmunna/agent-skillkit 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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/bin/ai-skills.js +5 -0
  4. package/dist/cli/commands/add.d.ts +2 -0
  5. package/dist/cli/commands/add.d.ts.map +1 -0
  6. package/dist/cli/commands/add.js +66 -0
  7. package/dist/cli/commands/add.js.map +1 -0
  8. package/dist/cli/commands/doctor.d.ts +2 -0
  9. package/dist/cli/commands/doctor.d.ts.map +1 -0
  10. package/dist/cli/commands/doctor.js +33 -0
  11. package/dist/cli/commands/doctor.js.map +1 -0
  12. package/dist/cli/commands/init.d.ts +10 -0
  13. package/dist/cli/commands/init.d.ts.map +1 -0
  14. package/dist/cli/commands/init.js +145 -0
  15. package/dist/cli/commands/init.js.map +1 -0
  16. package/dist/cli/commands/list.d.ts +5 -0
  17. package/dist/cli/commands/list.d.ts.map +1 -0
  18. package/dist/cli/commands/list.js +55 -0
  19. package/dist/cli/commands/list.js.map +1 -0
  20. package/dist/cli/commands/update.d.ts +2 -0
  21. package/dist/cli/commands/update.d.ts.map +1 -0
  22. package/dist/cli/commands/update.js +49 -0
  23. package/dist/cli/commands/update.js.map +1 -0
  24. package/dist/cli/commands/validate.d.ts +2 -0
  25. package/dist/cli/commands/validate.d.ts.map +1 -0
  26. package/dist/cli/commands/validate.js +22 -0
  27. package/dist/cli/commands/validate.js.map +1 -0
  28. package/dist/cli/index.d.ts +2 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +49 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/prompts/agent-selector.d.ts +3 -0
  33. package/dist/cli/prompts/agent-selector.d.ts.map +1 -0
  34. package/dist/cli/prompts/agent-selector.js +23 -0
  35. package/dist/cli/prompts/agent-selector.js.map +1 -0
  36. package/dist/cli/prompts/stack-selector.d.ts +3 -0
  37. package/dist/cli/prompts/stack-selector.d.ts.map +1 -0
  38. package/dist/cli/prompts/stack-selector.js +60 -0
  39. package/dist/cli/prompts/stack-selector.js.map +1 -0
  40. package/dist/core/config-manager.d.ts +20 -0
  41. package/dist/core/config-manager.d.ts.map +1 -0
  42. package/dist/core/config-manager.js +107 -0
  43. package/dist/core/config-manager.js.map +1 -0
  44. package/dist/core/detector.d.ts +3 -0
  45. package/dist/core/detector.d.ts.map +1 -0
  46. package/dist/core/detector.js +50 -0
  47. package/dist/core/detector.js.map +1 -0
  48. package/dist/core/doctor.d.ts +12 -0
  49. package/dist/core/doctor.d.ts.map +1 -0
  50. package/dist/core/doctor.js +102 -0
  51. package/dist/core/doctor.js.map +1 -0
  52. package/dist/core/skill-registry.d.ts +11 -0
  53. package/dist/core/skill-registry.d.ts.map +1 -0
  54. package/dist/core/skill-registry.js +174 -0
  55. package/dist/core/skill-registry.js.map +1 -0
  56. package/dist/core/skill-resolver.d.ts +3 -0
  57. package/dist/core/skill-resolver.d.ts.map +1 -0
  58. package/dist/core/skill-resolver.js +36 -0
  59. package/dist/core/skill-resolver.js.map +1 -0
  60. package/dist/core/validator.d.ts +13 -0
  61. package/dist/core/validator.d.ts.map +1 -0
  62. package/dist/core/validator.js +99 -0
  63. package/dist/core/validator.js.map +1 -0
  64. package/dist/generators/agent-installer.d.ts +5 -0
  65. package/dist/generators/agent-installer.d.ts.map +1 -0
  66. package/dist/generators/agent-installer.js +20 -0
  67. package/dist/generators/agent-installer.js.map +1 -0
  68. package/dist/generators/agents-md.d.ts +3 -0
  69. package/dist/generators/agents-md.d.ts.map +1 -0
  70. package/dist/generators/agents-md.js +70 -0
  71. package/dist/generators/agents-md.js.map +1 -0
  72. package/dist/generators/claude-md.d.ts +3 -0
  73. package/dist/generators/claude-md.d.ts.map +1 -0
  74. package/dist/generators/claude-md.js +47 -0
  75. package/dist/generators/claude-md.js.map +1 -0
  76. package/dist/generators/skill-generator.d.ts +5 -0
  77. package/dist/generators/skill-generator.d.ts.map +1 -0
  78. package/dist/generators/skill-generator.js +34 -0
  79. package/dist/generators/skill-generator.js.map +1 -0
  80. package/dist/generators/workflows.d.ts +3 -0
  81. package/dist/generators/workflows.d.ts.map +1 -0
  82. package/dist/generators/workflows.js +57 -0
  83. package/dist/generators/workflows.js.map +1 -0
  84. package/dist/index.d.ts +13 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +13 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/types/index.d.ts +55 -0
  89. package/dist/types/index.d.ts.map +1 -0
  90. package/dist/types/index.js +2 -0
  91. package/dist/types/index.js.map +1 -0
  92. package/dist/utils/file-utils.d.ts +12 -0
  93. package/dist/utils/file-utils.d.ts.map +1 -0
  94. package/dist/utils/file-utils.js +39 -0
  95. package/dist/utils/file-utils.js.map +1 -0
  96. package/dist/utils/logger.d.ts +10 -0
  97. package/dist/utils/logger.d.ts.map +1 -0
  98. package/dist/utils/logger.js +11 -0
  99. package/dist/utils/logger.js.map +1 -0
  100. package/package.json +73 -0
  101. package/skills/clean-architecture/SKILL.md +324 -0
  102. package/skills/express-mvc-prisma/SKILL.md +168 -0
  103. package/skills/express-mvc-prisma/references/auth.md +190 -0
  104. package/skills/express-mvc-prisma/references/boilerplate.md +196 -0
  105. package/skills/express-mvc-prisma/references/error-handling.md +121 -0
  106. package/skills/express-mvc-prisma/references/module-scaffold.md +253 -0
  107. package/skills/express-mvc-prisma/references/prisma-setup.md +97 -0
  108. package/skills/express-mvc-prisma/references/response-helpers.md +157 -0
  109. package/skills/express-mvc-prisma/references/zod-validation.md +157 -0
  110. package/skills/fastify-rest/SKILL.md +287 -0
  111. package/skills/mongoose-odm/SKILL.md +281 -0
  112. package/skills/nextjs-fullstack/SKILL.md +328 -0
  113. package/skills/nextjs-fullstack/references/auth.md +270 -0
  114. package/skills/nextjs-fullstack/references/caching.md +157 -0
  115. package/skills/nextjs-fullstack/references/route-handlers.md +194 -0
  116. package/skills/nextjs-fullstack/references/server-actions.md +214 -0
  117. package/skills/nextjs-fullstack/references/server-components.md +190 -0
  118. package/skills/node-base/SKILL.md +139 -0
  119. package/skills/prisma-orm/SKILL.md +334 -0
  120. package/skills/react-feature-arch/SKILL.md +208 -0
  121. package/skills/react-feature-arch/references/api-layer.md +110 -0
  122. package/skills/react-feature-arch/references/components.md +192 -0
  123. package/skills/react-feature-arch/references/data-fetching.md +198 -0
  124. package/skills/react-feature-arch/references/forms.md +194 -0
  125. package/skills/react-feature-arch/references/routing.md +148 -0
  126. package/skills/react-feature-arch/references/state-management.md +107 -0
  127. package/skills/tailwind-css/SKILL.md +236 -0
  128. package/skills/tailwind-css/references/components.md +340 -0
  129. package/skills/tailwind-css/references/design-tokens.md +230 -0
  130. package/skills/tailwind-css/references/patterns.md +375 -0
  131. package/skills/tailwind-css/references/setup.md +165 -0
  132. package/skills/zod-validation/SKILL.md +267 -0
@@ -0,0 +1,190 @@
1
+ # Server Components Reference
2
+
3
+ ---
4
+
5
+ ## Decision: Server vs Client Component
6
+
7
+ ```
8
+ Does the component need:
9
+ useState / useEffect / useRef / useContext? → 'use client'
10
+ onClick / onChange / onSubmit? → 'use client'
11
+ window / localStorage / document? → 'use client'
12
+ useActionState / useOptimistic / useFormStatus? → 'use client'
13
+ Third-party client-only library? → 'use client'
14
+ ─────────────────────────────────────────────
15
+ None of the above? → Server Component (default)
16
+ ```
17
+
18
+ **Push 'use client' as far down the tree as possible.** A page can be a Server Component that renders one or two leaf Client Components for interactivity.
19
+
20
+ ---
21
+
22
+ ## Data Fetching in Server Components
23
+
24
+ Fetch directly — no useEffect, no loading state, no API client:
25
+
26
+ ```tsx
27
+ // app/(dashboard)/users/page.tsx
28
+ import { db } from '@/lib/db';
29
+ import { auth } from '@/lib/auth';
30
+ import { notFound } from 'next/navigation';
31
+ import { UserList } from '@/features/users/components/UserList';
32
+ import { CreateButton } from '@/features/users/components/CreateButton';
33
+
34
+ export default async function UsersPage() {
35
+ const session = await auth();
36
+ if (!session) redirect('/auth/login');
37
+
38
+ const users = await db.user.findMany({
39
+ select: { id: true, name: true, email: true, role: true, createdAt: true },
40
+ orderBy: { createdAt: 'desc' },
41
+ });
42
+
43
+ return (
44
+ <div>
45
+ <div className="header">
46
+ <h1>Users</h1>
47
+ <CreateButton /> {/* Client Component */}
48
+ </div>
49
+ <UserList users={users} />
50
+ </div>
51
+ );
52
+ }
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Parallel Data Fetching
58
+
59
+ Use `Promise.all` for independent queries — runs them concurrently:
60
+
61
+ ```tsx
62
+ export default async function DashboardPage() {
63
+ const [users, products, stats] = await Promise.all([
64
+ db.user.count(),
65
+ db.product.count(),
66
+ getStats(),
67
+ ]);
68
+
69
+ return <Dashboard users={users} products={products} stats={stats} />;
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Dynamic Route — params as Promise (Next.js 15)
76
+
77
+ ```tsx
78
+ // app/(dashboard)/users/[id]/page.tsx
79
+ import { notFound } from 'next/navigation';
80
+ import { db } from '@/lib/db';
81
+
82
+ interface Props {
83
+ params: Promise<{ id: string }>;
84
+ }
85
+
86
+ export default async function UserDetailPage({ params }: Props) {
87
+ const { id } = await params;
88
+
89
+ const user = await db.user.findUnique({
90
+ where: { id },
91
+ select: { id: true, name: true, email: true, role: true, createdAt: true },
92
+ });
93
+
94
+ if (!user) notFound();
95
+
96
+ return <UserDetail user={user} />;
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Streaming with Suspense
103
+
104
+ Wrap slow data sources in `<Suspense>` to stream content progressively:
105
+
106
+ ```tsx
107
+ // app/(dashboard)/users/page.tsx
108
+ import { Suspense } from 'react';
109
+
110
+ export default function UsersPage() {
111
+ return (
112
+ <div>
113
+ <h1>Users</h1>
114
+ {/* Header renders immediately; list streams in */}
115
+ <Suspense fallback={<UserListSkeleton />}>
116
+ <UserListAsync />
117
+ </Suspense>
118
+ </div>
119
+ );
120
+ }
121
+
122
+ // Separate async component wraps the slow fetch
123
+ async function UserListAsync() {
124
+ const users = await db.user.findMany(); // waits here, not in the parent
125
+ return <UserList users={users} />;
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Passing Server Data to Client Components
132
+
133
+ Server Components can pass serializable props to Client Components:
134
+
135
+ ```tsx
136
+ // Server Component (parent)
137
+ export default async function UsersPage() {
138
+ const users = await db.user.findMany();
139
+
140
+ return (
141
+ <UserDataTable
142
+ users={users} // serialized and sent to client
143
+ initialPage={1}
144
+ />
145
+ );
146
+ }
147
+
148
+ // 'use client' (child) — receives pre-fetched data
149
+ 'use client';
150
+ export function UserDataTable({ users, initialPage }: Props) {
151
+ const [page, setPage] = useState(initialPage);
152
+ // uses users from server + client-side pagination
153
+ }
154
+ ```
155
+
156
+ **Cannot pass:** functions, class instances, non-serializable objects.
157
+
158
+ ---
159
+
160
+ ## Server Component Composition Patterns
161
+
162
+ ```tsx
163
+ // Composition: Server wraps Client
164
+ // ✅ Works — Server Component passes RSC children to Client
165
+ export default async function Layout({ children }: { children: React.ReactNode }) {
166
+ const session = await auth();
167
+ return (
168
+ <ClientSidebar session={session}>
169
+ {children}
170
+ </ClientSidebar>
171
+ );
172
+ }
173
+
174
+ // ✅ Works — Server Component imported in another Server Component
175
+ import { ServerNav } from '@/components/server/ServerNav';
176
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
177
+ return (
178
+ <html>
179
+ <body>
180
+ <ServerNav />
181
+ {children}
182
+ </body>
183
+ </html>
184
+ );
185
+ }
186
+
187
+ // ❌ Cannot — import Server Component inside 'use client' component
188
+ 'use client';
189
+ import { ServerComponent } from './ServerComponent'; // error: server-only
190
+ ```
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: node-base
3
+ version: 1.0.0
4
+ description: >
5
+ Base Node.js development standards. Apply this skill to every Node.js project regardless of framework. Covers async patterns, error handling foundations, environment configuration, file/folder conventions, logging, security defaults, and code quality rules.
6
+ stack: [node]
7
+ depends: []
8
+ ---
9
+
10
+ # Node.js Base Skill
11
+
12
+ Baseline standards for every Node.js project. Framework-agnostic. Superseded by more specific skills (express-mvc-prisma, fastify-rest) when those are active.
13
+
14
+ ---
15
+
16
+ ## Project Conventions
17
+
18
+ ### Module system
19
+ - Use ES Modules (`"type": "module"` in package.json) for new projects
20
+ - Use `.js` extensions in all import paths
21
+ - Never mix `require()` and `import` in the same codebase
22
+
23
+ ### File naming
24
+ - `kebab-case` for all files and directories
25
+ - No spaces, no underscores in file names
26
+ - Test files: `*.test.js` or `*.spec.js` adjacent to source
27
+
28
+ ### Folder structure (generic)
29
+ ```
30
+ src/
31
+ ├── config/ # Env, DB, third-party clients
32
+ ├── modules/ # Feature-grouped domain logic
33
+ ├── middlewares/ # Cross-cutting concerns
34
+ ├── utils/ # Pure utilities (no side effects)
35
+ └── types/ # Shared type definitions (TS projects)
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Environment Variables
41
+
42
+ Always validate env at startup. Never read `process.env` outside the config module.
43
+
44
+ ```js
45
+ // src/config/env.js — minimal template
46
+ import { z } from 'zod';
47
+ import 'dotenv/config';
48
+
49
+ const schema = z.object({
50
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
51
+ PORT: z.coerce.number().default(3000),
52
+ });
53
+
54
+ const result = schema.safeParse(process.env);
55
+ if (!result.success) {
56
+ console.error('❌ Missing env vars:', result.error.flatten().fieldErrors);
57
+ process.exit(1);
58
+ }
59
+
60
+ export const env = result.data;
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Async Patterns
66
+
67
+ - Always `async/await` — no `.then()/.catch()` chains
68
+ - Never swallow errors with empty `catch` blocks
69
+ - Top-level `await` is supported in ES Modules
70
+
71
+ ```js
72
+ // Good
73
+ const data = await fetchData();
74
+
75
+ // Bad
76
+ fetchData().then(data => { ... });
77
+
78
+ // Never
79
+ try { ... } catch (e) {} // swallowed error
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Error Handling
85
+
86
+ - Create an `AppError` class for operational errors (expected, safe to expose to client)
87
+ - Unhandled rejections and exceptions must crash the process in production
88
+
89
+ ```js
90
+ // src/utils/AppError.js
91
+ export class AppError extends Error {
92
+ constructor(message, statusCode = 500) {
93
+ super(message);
94
+ this.statusCode = statusCode;
95
+ this.isOperational = true;
96
+ Error.captureStackTrace(this, this.constructor);
97
+ }
98
+ }
99
+
100
+ // Crash on unhandled rejections (in server.js / entry point)
101
+ process.on('unhandledRejection', (reason) => {
102
+ console.error('Unhandled Rejection:', reason);
103
+ process.exit(1);
104
+ });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Logging
110
+
111
+ - Use `console.error()` for errors (goes to stderr)
112
+ - Use `console.log()` for info (goes to stdout)
113
+ - Include context: `[module] message` format
114
+ - Never log passwords, tokens, or PII
115
+
116
+ ```js
117
+ console.error('[auth] Login failed:', email); // OK — no password
118
+ console.log('[server] Listening on port', port);
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Security Defaults
124
+
125
+ Always apply these in HTTP servers:
126
+ - Disable `X-Powered-By` header: `app.disable('x-powered-by')`
127
+ - Validate and sanitize all user input before using it
128
+ - Never use `eval()`, `new Function()`, or `vm.runInThisContext()` with user input
129
+ - Use `crypto.randomBytes()` or `crypto.randomUUID()` for tokens — never `Math.random()`
130
+
131
+ ---
132
+
133
+ ## Code Quality Rules
134
+
135
+ - Max function length: ~30 lines. Extract if longer
136
+ - One export concept per file
137
+ - Pure utility functions must be side-effect-free
138
+ - No `var` — always `const`, use `let` only when reassignment is required
139
+ - No `any` in TypeScript — use `unknown` and narrow
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: prisma-orm
3
+ version: 2.0.0
4
+ description: >
5
+ Apply this skill for any task involving Prisma ORM: designing schemas, writing migrations, querying data, handling relations, transactions, optimizing queries, or seeding. Triggers: "add Prisma to project", "design schema", "write a query", "handle Prisma errors", "transaction", "migrate database", "Prisma relations", "seeding", "Prisma v5".
6
+ stack: [prisma, postgresql]
7
+ depends: []
8
+ ---
9
+
10
+ # Prisma ORM v5 — Production Skill
11
+
12
+ **Version target:** Prisma v5 · PostgreSQL · Node.js 20+ (ESM)
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @prisma/client
20
+ npm install -D prisma
21
+ npx prisma init --datasource-provider postgresql
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Schema Conventions
27
+
28
+ ```prisma
29
+ generator client {
30
+ provider = "prisma-client-js"
31
+ }
32
+
33
+ datasource db {
34
+ provider = "postgresql"
35
+ url = env("DATABASE_URL")
36
+ }
37
+
38
+ model User {
39
+ id String @id @default(uuid()) // UUID always, never autoincrement
40
+ email String @unique
41
+ name String
42
+ password String
43
+ role Role @default(USER) // enum for fixed value sets
44
+ posts Post[]
45
+ createdAt DateTime @default(now()) // always add timestamps
46
+ updatedAt DateTime @updatedAt
47
+
48
+ @@map("users") // snake_case table name always
49
+ }
50
+
51
+ model Post {
52
+ id String @id @default(uuid())
53
+ title String
54
+ body String
55
+ published Boolean @default(false)
56
+ authorId String
57
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
58
+ createdAt DateTime @default(now())
59
+ updatedAt DateTime @updatedAt
60
+
61
+ @@index([authorId]) // index every foreign key
62
+ @@map("posts")
63
+ }
64
+
65
+ enum Role {
66
+ USER
67
+ ADMIN
68
+ MANAGER
69
+ }
70
+ ```
71
+
72
+ **Schema rules (non-negotiable):**
73
+ - `@id @default(uuid())` on every model — never `@default(autoincrement())`
74
+ - `createdAt` + `updatedAt` on every model
75
+ - `@@map("snake_case")` on every model
76
+ - `@@index([fk])` on every foreign key field
77
+ - Enums for any column with a fixed set of values
78
+
79
+ ---
80
+
81
+ ## Prisma Singleton
82
+
83
+ ```js
84
+ // src/config/db.js (Express) or src/lib/db.ts (Next.js)
85
+ import { PrismaClient } from '@prisma/client';
86
+ import { isDev } from './env.js';
87
+
88
+ const globalForPrisma = globalThis;
89
+
90
+ export const prisma =
91
+ globalForPrisma.prisma ??
92
+ new PrismaClient({ log: isDev ? ['query', 'warn', 'error'] : ['error'] });
93
+
94
+ if (isDev) globalForPrisma.prisma = prisma;
95
+
96
+ export default prisma;
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Service-Layer Queries (4-file pattern — no repository)
102
+
103
+ Services call Prisma directly. No separate repository file.
104
+
105
+ ```js
106
+ // modules/post/post.service.js
107
+ import prisma from '../../config/db.js';
108
+ import { Prisma } from '@prisma/client';
109
+ import { AppError } from '../../utils/AppError.js';
110
+
111
+ export const postService = {
112
+ async getAll({ page = 1, limit = 10, authorId } = {}) {
113
+ const skip = (page - 1) * limit;
114
+ const where = authorId ? { authorId } : {};
115
+
116
+ const [data, total] = await prisma.$transaction([
117
+ prisma.post.findMany({
118
+ where,
119
+ skip,
120
+ take: limit,
121
+ orderBy: { createdAt: 'desc' },
122
+ select: {
123
+ id: true, title: true, published: true, createdAt: true,
124
+ author: { select: { id: true, name: true } },
125
+ },
126
+ }),
127
+ prisma.post.count({ where }),
128
+ ]);
129
+
130
+ return { data, total, page, limit };
131
+ },
132
+
133
+ async getById(id) {
134
+ const post = await prisma.post.findUnique({
135
+ where: { id },
136
+ include: { author: { select: { id: true, name: true } } },
137
+ });
138
+ if (!post) throw new AppError('Post not found', 404);
139
+ return post;
140
+ },
141
+
142
+ async create(data) {
143
+ try {
144
+ return await prisma.post.create({ data });
145
+ } catch (err) {
146
+ if (err instanceof Prisma.PrismaClientKnownRequestError) {
147
+ if (err.code === 'P2002') throw new AppError('Duplicate entry', 409);
148
+ if (err.code === 'P2003') throw new AppError('Related record not found', 400);
149
+ }
150
+ throw err;
151
+ }
152
+ },
153
+
154
+ async update(id, data) {
155
+ try {
156
+ return await prisma.post.update({ where: { id }, data });
157
+ } catch (err) {
158
+ if (err instanceof Prisma.PrismaClientKnownRequestError) {
159
+ if (err.code === 'P2025') throw new AppError('Post not found', 404);
160
+ }
161
+ throw err;
162
+ }
163
+ },
164
+
165
+ async remove(id) {
166
+ await prisma.post.delete({ where: { id } });
167
+ },
168
+ };
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Prisma Error Codes — Handle in Service Layer
174
+
175
+ | Code | Meaning | Response |
176
+ |------|---------|--------|
177
+ | P2002 | Unique constraint violation | 409 Conflict |
178
+ | P2025 | Record not found (update/delete) | 404 Not Found |
179
+ | P2003 | Foreign key constraint failed | 400 Bad Request |
180
+ | P2014 | Relation violation | 400 Bad Request |
181
+
182
+ The global `errorHandler` also catches raw `PrismaClientKnownRequestError` as a fallback.
183
+
184
+ ---
185
+
186
+ ## Transactions
187
+
188
+ Use `$transaction` for multi-step writes that must be atomic:
189
+
190
+ ```js
191
+ // Batch (parallel) — read count + list
192
+ const [data, total] = await prisma.$transaction([
193
+ prisma.post.findMany({ skip, take }),
194
+ prisma.post.count(),
195
+ ]);
196
+
197
+ // Interactive (sequential) — reads depend on previous writes
198
+ const result = await prisma.$transaction(async (tx) => {
199
+ const order = await tx.order.create({ data: orderData });
200
+
201
+ await tx.product.update({
202
+ where: { id: orderData.productId },
203
+ data: { stock: { decrement: orderData.quantity } },
204
+ });
205
+
206
+ await tx.payment.create({
207
+ data: { orderId: order.id, amount: orderData.total },
208
+ });
209
+
210
+ return order;
211
+ });
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Query Optimization
217
+
218
+ ```js
219
+ // Select only needed fields — never return password or secrets
220
+ prisma.user.findMany({
221
+ select: { id: true, name: true, email: true, role: true },
222
+ })
223
+
224
+ // Include relations with select — avoid N+1 and fetching unnecessary data
225
+ prisma.post.findMany({
226
+ include: {
227
+ author: { select: { id: true, name: true } },
228
+ _count: { select: { comments: true } }, // count without loading records
229
+ },
230
+ })
231
+
232
+ // Cursor-based pagination for large datasets
233
+ prisma.post.findMany({
234
+ take: 10,
235
+ skip: 1, // skip the cursor itself
236
+ cursor: { id: lastId },
237
+ orderBy: { createdAt: 'asc' },
238
+ })
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Relation Patterns
244
+
245
+ ```prisma
246
+ // One-to-many
247
+ model Category {
248
+ id String @id @default(uuid())
249
+ name String
250
+ posts Post[]
251
+
252
+ @@map("categories")
253
+ }
254
+
255
+ model Post {
256
+ categoryId String?
257
+ category Category? @relation(fields: [categoryId], references: [id])
258
+
259
+ @@index([categoryId])
260
+ @@map("posts")
261
+ }
262
+
263
+ // Many-to-many (explicit join table)
264
+ model Post {
265
+ tags PostTag[]
266
+ }
267
+
268
+ model Tag {
269
+ posts PostTag[]
270
+ }
271
+
272
+ model PostTag {
273
+ postId String
274
+ tagId String
275
+ post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
276
+ tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
277
+
278
+ @@id([postId, tagId])
279
+ @@map("post_tags")
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Migration Commands
286
+
287
+ ```bash
288
+ npx prisma migrate dev --name <description> # create + apply (dev only)
289
+ npx prisma migrate deploy # apply pending (production)
290
+ npx prisma migrate reset # reset + reseed (dev only)
291
+ npx prisma generate # regenerate Prisma Client
292
+ npx prisma studio # GUI browser
293
+ npx prisma db seed # run seed file
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Seeding
299
+
300
+ ```js
301
+ // prisma/seed.js
302
+ import { PrismaClient } from '@prisma/client';
303
+ import bcrypt from 'bcrypt';
304
+
305
+ const prisma = new PrismaClient();
306
+
307
+ async function main() {
308
+ const passwordHash = await bcrypt.hash('password123', 12);
309
+
310
+ await prisma.user.upsert({
311
+ where: { email: 'admin@example.com' },
312
+ update: {},
313
+ create: {
314
+ email: 'admin@example.com',
315
+ name: 'Admin',
316
+ password: passwordHash,
317
+ role: 'ADMIN',
318
+ },
319
+ });
320
+
321
+ console.log('Seeding complete');
322
+ }
323
+
324
+ main()
325
+ .catch(console.error)
326
+ .finally(() => prisma.$disconnect());
327
+ ```
328
+
329
+ In `package.json`:
330
+ ```json
331
+ {
332
+ "prisma": { "seed": "node prisma/seed.js" }
333
+ }
334
+ ```