@kood/claude-code 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +124 -124
- package/templates/.claude/agents/dependency-manager.md +85 -85
- package/templates/.claude/agents/deployment-validator.md +56 -56
- package/templates/.claude/agents/git-operator.md +64 -64
- package/templates/.claude/agents/implementation-executor.md +95 -95
- package/templates/.claude/agents/ko-to-en-translator.md +74 -0
- package/templates/.claude/agents/lint-fixer.md +78 -78
- package/templates/.claude/agents/refactor-advisor.md +122 -122
- package/templates/.claude/commands/agent-creator.md +185 -185
- package/templates/.claude/commands/bug-fix.md +193 -193
- package/templates/.claude/commands/command-creator.md +54 -54
- package/templates/.claude/commands/docs-creator.md +57 -57
- package/templates/.claude/commands/docs-refactor.md +26 -26
- package/templates/.claude/commands/execute.md +12 -12
- package/templates/.claude/commands/git-all.md +32 -32
- package/templates/.claude/commands/git-session.md +42 -42
- package/templates/.claude/commands/git.md +34 -34
- package/templates/.claude/commands/lint-fix.md +138 -138
- package/templates/.claude/commands/lint-init.md +61 -61
- package/templates/.claude/commands/plan.md +260 -260
- package/templates/.claude/commands/prd.md +24 -24
- package/templates/.claude/commands/pre-deploy.md +109 -109
- package/templates/.claude/commands/refactor.md +147 -147
- package/templates/.claude/commands/version-update.md +17 -17
- package/templates/hono/CLAUDE.md +27 -27
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +228 -0
- package/templates/nextjs/docs/design.md +558 -0
- package/templates/nextjs/docs/guides/conventions.md +343 -0
- package/templates/nextjs/docs/guides/getting-started.md +367 -0
- package/templates/nextjs/docs/guides/routes.md +342 -0
- package/templates/nextjs/docs/library/better-auth/index.md +541 -0
- package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
- package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
- package/templates/nextjs/docs/library/nextjs/index.md +291 -0
- package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
- package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
- package/templates/nextjs/docs/library/prisma/config.md +77 -0
- package/templates/nextjs/docs/library/prisma/crud.md +90 -0
- package/templates/nextjs/docs/library/prisma/index.md +73 -0
- package/templates/nextjs/docs/library/prisma/relations.md +69 -0
- package/templates/nextjs/docs/library/prisma/schema.md +98 -0
- package/templates/nextjs/docs/library/prisma/setup.md +49 -0
- package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
- package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
- package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
- package/templates/nextjs/docs/library/zod/index.md +56 -0
- package/templates/nextjs/docs/library/zod/transforms.md +51 -0
- package/templates/nextjs/docs/library/zod/validation.md +70 -0
- package/templates/npx/CLAUDE.md +37 -37
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +53 -49
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +63 -35
- package/templates/tanstack-start/docs/guides/routes.md +61 -42
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/better-auth/index.md +68 -68
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/index.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/t3-env/index.md +21 -160
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +15 -15
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/index.md +8 -8
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Server Actions
|
|
2
|
+
|
|
3
|
+
> Type-safe server functions (React 19 Server Actions)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
### File-level Declaration
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// app/actions.ts
|
|
13
|
+
"use server"
|
|
14
|
+
|
|
15
|
+
import { z } from "zod"
|
|
16
|
+
import { revalidatePath } from "next/cache"
|
|
17
|
+
|
|
18
|
+
export async function createPost(formData: FormData) {
|
|
19
|
+
const title = formData.get("title") as string
|
|
20
|
+
const content = formData.get("content") as string
|
|
21
|
+
|
|
22
|
+
const post = await prisma.post.create({
|
|
23
|
+
data: { title, content },
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
revalidatePath("/posts")
|
|
27
|
+
return post
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Inline Declaration
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// app/posts/page.tsx
|
|
35
|
+
export default function PostsPage() {
|
|
36
|
+
async function createPost(formData: FormData) {
|
|
37
|
+
"use server"
|
|
38
|
+
|
|
39
|
+
const title = formData.get("title") as string
|
|
40
|
+
await prisma.post.create({ data: { title } })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return <form action={createPost}>...</form>
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Zod Validation
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
"use server"
|
|
53
|
+
|
|
54
|
+
import { z } from "zod"
|
|
55
|
+
|
|
56
|
+
const createPostSchema = z.object({
|
|
57
|
+
title: z.string().min(1).max(100),
|
|
58
|
+
content: z.string().min(1),
|
|
59
|
+
published: z.boolean().default(false),
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
export async function createPost(formData: FormData) {
|
|
63
|
+
const parsed = createPostSchema.parse({
|
|
64
|
+
title: formData.get("title"),
|
|
65
|
+
content: formData.get("content"),
|
|
66
|
+
published: formData.get("published") === "on",
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const post = await prisma.post.create({ data: parsed })
|
|
70
|
+
revalidatePath("/posts")
|
|
71
|
+
return post
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Authentication
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
"use server"
|
|
81
|
+
|
|
82
|
+
import { auth } from "@/lib/auth"
|
|
83
|
+
import { redirect } from "next/navigation"
|
|
84
|
+
|
|
85
|
+
export async function deletePost(id: string) {
|
|
86
|
+
const session = await auth()
|
|
87
|
+
|
|
88
|
+
if (!session?.user) {
|
|
89
|
+
redirect("/login")
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await prisma.post.delete({
|
|
93
|
+
where: {
|
|
94
|
+
id,
|
|
95
|
+
userId: session.user.id, // Delete only own posts
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
revalidatePath("/posts")
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Error Handling
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
"use server"
|
|
109
|
+
|
|
110
|
+
export async function updatePost(id: string, data: PostInput) {
|
|
111
|
+
try {
|
|
112
|
+
const post = await prisma.post.update({ where: { id }, data })
|
|
113
|
+
revalidatePath(`/posts/${id}`)
|
|
114
|
+
return { success: true, post }
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof z.ZodError) {
|
|
117
|
+
return { success: false, errors: error.errors }
|
|
118
|
+
}
|
|
119
|
+
return { success: false, message: "Update failed" }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Client Usage
|
|
127
|
+
|
|
128
|
+
### Form Action
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
"use client"
|
|
132
|
+
|
|
133
|
+
import { createPost } from "@/actions/posts"
|
|
134
|
+
|
|
135
|
+
export function CreatePostForm() {
|
|
136
|
+
return (
|
|
137
|
+
<form action={createPost}>
|
|
138
|
+
<input name="title" required />
|
|
139
|
+
<textarea name="content" required />
|
|
140
|
+
<button type="submit">Submit</button>
|
|
141
|
+
</form>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### TanStack Query
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
"use client"
|
|
150
|
+
|
|
151
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
|
152
|
+
import { createPost } from "@/actions/posts"
|
|
153
|
+
|
|
154
|
+
export function CreatePostForm() {
|
|
155
|
+
const queryClient = useQueryClient()
|
|
156
|
+
|
|
157
|
+
const mutation = useMutation({
|
|
158
|
+
mutationFn: async (formData: FormData) => {
|
|
159
|
+
return createPost(formData)
|
|
160
|
+
},
|
|
161
|
+
onSuccess: () => {
|
|
162
|
+
queryClient.invalidateQueries({ queryKey: ["posts"] })
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<form
|
|
168
|
+
onSubmit={(e) => {
|
|
169
|
+
e.preventDefault()
|
|
170
|
+
const formData = new FormData(e.currentTarget)
|
|
171
|
+
mutation.mutate(formData)
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<input name="title" required />
|
|
175
|
+
<button type="submit" disabled={mutation.isPending}>
|
|
176
|
+
{mutation.isPending ? "Submitting..." : "Submit"}
|
|
177
|
+
</button>
|
|
178
|
+
</form>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## useFormState (React 19)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
"use server"
|
|
189
|
+
|
|
190
|
+
export async function createPost(prevState: any, formData: FormData) {
|
|
191
|
+
const title = formData.get("title") as string
|
|
192
|
+
|
|
193
|
+
if (!title) {
|
|
194
|
+
return { error: "Please enter a title" }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const post = await prisma.post.create({ data: { title } })
|
|
198
|
+
return { success: true, post }
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
"use client"
|
|
204
|
+
|
|
205
|
+
import { useFormState } from "react-dom"
|
|
206
|
+
import { createPost } from "@/actions/posts"
|
|
207
|
+
|
|
208
|
+
export function CreatePostForm() {
|
|
209
|
+
const [state, formAction] = useFormState(createPost, null)
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<form action={formAction}>
|
|
213
|
+
<input name="title" />
|
|
214
|
+
{state?.error && <p>{state.error}</p>}
|
|
215
|
+
<button type="submit">Submit</button>
|
|
216
|
+
</form>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Cache Invalidation
|
|
224
|
+
|
|
225
|
+
### revalidatePath
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
"use server"
|
|
229
|
+
|
|
230
|
+
import { revalidatePath } from "next/cache"
|
|
231
|
+
|
|
232
|
+
export async function createPost(data: PostInput) {
|
|
233
|
+
const post = await prisma.post.create({ data })
|
|
234
|
+
|
|
235
|
+
// Invalidate specific path cache
|
|
236
|
+
revalidatePath("/posts")
|
|
237
|
+
revalidatePath(`/posts/${post.id}`)
|
|
238
|
+
|
|
239
|
+
// Invalidate all caches including layouts
|
|
240
|
+
revalidatePath("/posts", "layout")
|
|
241
|
+
|
|
242
|
+
return post
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### revalidateTag
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// Set tag when fetching data
|
|
250
|
+
const posts = await fetch("https://api.example.com/posts", {
|
|
251
|
+
next: { tags: ["posts"] },
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Invalidate tag in Server Action
|
|
255
|
+
"use server"
|
|
256
|
+
|
|
257
|
+
import { revalidateTag } from "next/cache"
|
|
258
|
+
|
|
259
|
+
export async function createPost(data: PostInput) {
|
|
260
|
+
const post = await prisma.post.create({ data })
|
|
261
|
+
revalidateTag("posts") // Invalidate "posts" tag cache
|
|
262
|
+
return post
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## redirect
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
"use server"
|
|
272
|
+
|
|
273
|
+
import { redirect } from "next/navigation"
|
|
274
|
+
|
|
275
|
+
export async function createPost(formData: FormData) {
|
|
276
|
+
const post = await prisma.post.create({
|
|
277
|
+
data: {
|
|
278
|
+
title: formData.get("title") as string,
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
redirect(`/posts/${post.id}`) // Navigate to page
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## File Upload
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
"use server"
|
|
292
|
+
|
|
293
|
+
import { writeFile } from "fs/promises"
|
|
294
|
+
import { join } from "path"
|
|
295
|
+
|
|
296
|
+
export async function uploadFile(formData: FormData) {
|
|
297
|
+
const file = formData.get("file") as File
|
|
298
|
+
|
|
299
|
+
if (!file) {
|
|
300
|
+
throw new Error("No file provided")
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const bytes = await file.arrayBuffer()
|
|
304
|
+
const buffer = Buffer.from(bytes)
|
|
305
|
+
|
|
306
|
+
const path = join(process.cwd(), "public", "uploads", file.name)
|
|
307
|
+
await writeFile(path, buffer)
|
|
308
|
+
|
|
309
|
+
return { url: `/uploads/${file.name}` }
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Best Practices
|
|
316
|
+
|
|
317
|
+
### ✅ DO
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
"use server"
|
|
321
|
+
|
|
322
|
+
// 1. Zod validation
|
|
323
|
+
const schema = z.object({ title: z.string().min(1) })
|
|
324
|
+
|
|
325
|
+
// 2. Authentication check
|
|
326
|
+
const session = await auth()
|
|
327
|
+
if (!session) throw new Error("Unauthorized")
|
|
328
|
+
|
|
329
|
+
// 3. try-catch
|
|
330
|
+
try {
|
|
331
|
+
const post = await prisma.post.create({ data })
|
|
332
|
+
revalidatePath("/posts")
|
|
333
|
+
return { success: true, post }
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return { success: false, message: "Creation failed" }
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### ❌ DON'T
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// 1. Defining Server Action in Client Component
|
|
343
|
+
"use client"
|
|
344
|
+
|
|
345
|
+
async function createPost() {
|
|
346
|
+
"use server" // ❌ Error
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 2. Using without validation
|
|
350
|
+
export async function createPost(formData: FormData) {
|
|
351
|
+
const title = formData.get("title") // ❌ Missing validation
|
|
352
|
+
await prisma.post.create({ data: { title } })
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// 3. Using without try-catch
|
|
356
|
+
export async function createPost(data: PostInput) {
|
|
357
|
+
const post = await prisma.post.create({ data }) // ❌ Missing error handling
|
|
358
|
+
return post
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## References
|
|
365
|
+
|
|
366
|
+
- [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Prisma - Cloudflare D1
|
|
2
|
+
|
|
3
|
+
SQLite-based serverless database. Different workflow from standard Prisma migrations.
|
|
4
|
+
|
|
5
|
+
⚠️ No transaction support | Cannot use prisma migrate - use wrangler instead | Preview status
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```prisma
|
|
10
|
+
// schema.prisma
|
|
11
|
+
generator client {
|
|
12
|
+
provider = "prisma-client"
|
|
13
|
+
output = "../src/generated/prisma"
|
|
14
|
+
runtime = "cloudflare" // required
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
datasource db {
|
|
18
|
+
provider = "sqlite"
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```jsonc
|
|
23
|
+
// wrangler.jsonc
|
|
24
|
+
{
|
|
25
|
+
"d1_databases": [{ "binding": "DB", "database_name": "my-db", "database_id": "ID" }]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { PrismaClient } from './generated/prisma'
|
|
33
|
+
import { PrismaD1 } from '@prisma/adapter-d1'
|
|
34
|
+
|
|
35
|
+
export interface Env { DB: D1Database }
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
39
|
+
const adapter = new PrismaD1(env.DB)
|
|
40
|
+
const prisma = new PrismaClient({ adapter })
|
|
41
|
+
const users = await prisma.user.findMany()
|
|
42
|
+
return new Response(JSON.stringify(users))
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Migration Workflow
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. Create D1
|
|
51
|
+
npx wrangler d1 create my-database
|
|
52
|
+
|
|
53
|
+
# 2. Create migration
|
|
54
|
+
npx wrangler d1 migrations create my-database init
|
|
55
|
+
|
|
56
|
+
# 3. Generate SQL (initial)
|
|
57
|
+
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script --output prisma/migrations/0001.sql
|
|
58
|
+
|
|
59
|
+
# 4. Generate SQL (subsequent)
|
|
60
|
+
npx prisma migrate diff --from-local-d1 --to-schema-datamodel prisma/schema.prisma --script
|
|
61
|
+
|
|
62
|
+
# 5. Apply
|
|
63
|
+
npx wrangler d1 migrations apply my-database --local # local
|
|
64
|
+
npx wrangler d1 migrations apply my-database --remote # production
|
|
65
|
+
|
|
66
|
+
# 6. Generate client
|
|
67
|
+
npx prisma generate
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Limitations
|
|
71
|
+
|
|
72
|
+
| Feature | Regular SQLite | D1 |
|
|
73
|
+
|---------|----------------|-----|
|
|
74
|
+
| Migration | prisma migrate | wrangler d1 |
|
|
75
|
+
| Transaction | ✅ | ❌ |
|
|
76
|
+
| Connection | Direct | HTTP adapter |
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Prisma - Config File
|
|
2
|
+
|
|
3
|
+
Prisma v7 `prisma.config.ts` configuration.
|
|
4
|
+
|
|
5
|
+
## Multi-File Schema (Required)
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// prisma.config.ts
|
|
9
|
+
import 'dotenv/config'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import { defineConfig, env } from 'prisma/config'
|
|
12
|
+
|
|
13
|
+
export default defineConfig({
|
|
14
|
+
schema: path.join('prisma', 'schema'), // folder path!
|
|
15
|
+
migrations: {
|
|
16
|
+
path: 'prisma/migrations',
|
|
17
|
+
seed: 'tsx prisma/seed.ts',
|
|
18
|
+
},
|
|
19
|
+
datasource: {
|
|
20
|
+
url: env('DATABASE_URL'),
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Folder Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
project/
|
|
29
|
+
├── prisma.config.ts
|
|
30
|
+
├── prisma/
|
|
31
|
+
│ ├── schema/
|
|
32
|
+
│ │ ├── +base.prisma # datasource, generator
|
|
33
|
+
│ │ ├── +enum.prisma # enum definitions
|
|
34
|
+
│ │ └── user.prisma # models
|
|
35
|
+
│ └── migrations/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## +base.prisma
|
|
39
|
+
|
|
40
|
+
```prisma
|
|
41
|
+
datasource db {
|
|
42
|
+
provider = "postgresql"
|
|
43
|
+
url = env("DATABASE_URL")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
generator client {
|
|
47
|
+
provider = "prisma-client"
|
|
48
|
+
output = "../../generated/prisma"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configuration Options
|
|
53
|
+
|
|
54
|
+
| Option | Description |
|
|
55
|
+
|--------|-------------|
|
|
56
|
+
| `schema` | Schema folder path |
|
|
57
|
+
| `datasource.url` | Database URL (required) |
|
|
58
|
+
| `datasource.shadowDatabaseUrl` | Shadow database URL |
|
|
59
|
+
| `migrations.path` | Migrations folder |
|
|
60
|
+
| `migrations.seed` | Seed command |
|
|
61
|
+
|
|
62
|
+
## Seed File
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// prisma/seed.ts
|
|
66
|
+
import { PrismaClient } from '../generated/prisma'
|
|
67
|
+
|
|
68
|
+
const prisma = new PrismaClient()
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
await prisma.user.create({
|
|
72
|
+
data: { email: 'admin@example.com', name: 'Admin', role: 'ADMIN' },
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
main().catch(console.error).finally(() => prisma.$disconnect())
|
|
77
|
+
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Prisma - CRUD Operations
|
|
2
|
+
|
|
3
|
+
## Create
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Single
|
|
7
|
+
const user = await prisma.user.create({
|
|
8
|
+
data: { email: 'alice@prisma.io', name: 'Alice' },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
// With relations
|
|
12
|
+
const user = await prisma.user.create({
|
|
13
|
+
data: {
|
|
14
|
+
email: 'bob@prisma.io',
|
|
15
|
+
posts: { create: [{ title: 'Hello' }, { title: 'World' }] },
|
|
16
|
+
},
|
|
17
|
+
include: { posts: true },
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// connectOrCreate
|
|
21
|
+
posts: { create: [{
|
|
22
|
+
title: 'Post',
|
|
23
|
+
categories: { connectOrCreate: [{ create: { name: 'Tech' }, where: { name: 'Tech' } }] },
|
|
24
|
+
}]}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Read
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Single
|
|
31
|
+
const user = await prisma.user.findUnique({ where: { email } })
|
|
32
|
+
|
|
33
|
+
// Multiple
|
|
34
|
+
const users = await prisma.user.findMany({ where: { name: 'Alice' } })
|
|
35
|
+
|
|
36
|
+
// With relations
|
|
37
|
+
const users = await prisma.user.findMany({ where: { role: 'ADMIN' }, include: { posts: true } })
|
|
38
|
+
|
|
39
|
+
// Select fields
|
|
40
|
+
const user = await prisma.user.findUnique({
|
|
41
|
+
where: { email },
|
|
42
|
+
select: { email: true, posts: { select: { title: true } } },
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Filter by relation
|
|
46
|
+
const users = await prisma.user.findMany({
|
|
47
|
+
where: { posts: { some: { published: false } } },
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Update
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Single
|
|
55
|
+
const user = await prisma.user.update({ where: { id }, data: { name: 'Updated' } })
|
|
56
|
+
|
|
57
|
+
// Multiple
|
|
58
|
+
await prisma.user.updateMany({ where: { role: 'USER' }, data: { role: 'ADMIN' } })
|
|
59
|
+
|
|
60
|
+
// Upsert
|
|
61
|
+
const user = await prisma.user.upsert({
|
|
62
|
+
where: { email },
|
|
63
|
+
update: { name: 'Updated' },
|
|
64
|
+
create: { email, name: 'New' },
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Delete
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
await prisma.user.delete({ where: { id } })
|
|
72
|
+
await prisma.user.deleteMany({}) // all
|
|
73
|
+
await prisma.post.deleteMany({ where: { published: false } }) // conditional
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Filter Operators
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// String
|
|
80
|
+
{ contains: 'prisma', startsWith: 'A', endsWith: 'io' }
|
|
81
|
+
|
|
82
|
+
// Number
|
|
83
|
+
{ gt: 18, gte: 18, lt: 65, lte: 65 }
|
|
84
|
+
|
|
85
|
+
// Array
|
|
86
|
+
{ in: [1, 2, 3], notIn: [4, 5] }
|
|
87
|
+
|
|
88
|
+
// Logical
|
|
89
|
+
{ OR: [...], AND: [...], NOT: {...} }
|
|
90
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Prisma
|
|
2
|
+
|
|
3
|
+
> **Version**: 7.x | Node.js/TypeScript ORM
|
|
4
|
+
|
|
5
|
+
@setup.md
|
|
6
|
+
@config.md
|
|
7
|
+
@schema.md
|
|
8
|
+
@crud.md
|
|
9
|
+
@relations.md
|
|
10
|
+
@transactions.md
|
|
11
|
+
@cloudflare-d1.md
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Reference
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { PrismaClient } from './generated/prisma' // v7 path!
|
|
19
|
+
export const prisma = new PrismaClient()
|
|
20
|
+
|
|
21
|
+
// CRUD
|
|
22
|
+
const users = await prisma.user.findMany()
|
|
23
|
+
const user = await prisma.user.create({ data: { email, name } })
|
|
24
|
+
const updated = await prisma.user.update({ where: { id }, data: { name } })
|
|
25
|
+
const deleted = await prisma.user.delete({ where: { id } })
|
|
26
|
+
|
|
27
|
+
// With relations
|
|
28
|
+
const userWithPosts = await prisma.user.findUnique({
|
|
29
|
+
where: { id },
|
|
30
|
+
include: { posts: true },
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### v7 schema.prisma (⚠️ Important)
|
|
35
|
+
|
|
36
|
+
```prisma
|
|
37
|
+
generator client {
|
|
38
|
+
provider = "prisma-client" // v7! (not prisma-client-js)
|
|
39
|
+
output = "../generated/prisma" // output required!
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ⛔ Claude Code Forbidden
|
|
44
|
+
|
|
45
|
+
| Forbidden Actions |
|
|
46
|
+
|-------------------|
|
|
47
|
+
| Auto-running prisma db push |
|
|
48
|
+
| Auto-running prisma migrate |
|
|
49
|
+
| Auto-running prisma generate |
|
|
50
|
+
| Unauthorized schema.prisma modifications |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Prisma Client Setup
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// lib/prisma.ts
|
|
58
|
+
import { PrismaClient } from './generated/prisma'
|
|
59
|
+
|
|
60
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
61
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient({ log: ['query'] })
|
|
62
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Migration Commands
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx prisma migrate dev --name init # development migration
|
|
69
|
+
npx prisma migrate deploy # production migration
|
|
70
|
+
npx prisma db push # schema sync (dev only)
|
|
71
|
+
npx prisma generate # generate client
|
|
72
|
+
npx prisma studio # GUI
|
|
73
|
+
```
|