@kood/claude-code 0.3.8 → 0.3.9

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 (30) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/code-reviewer.md +16 -1
  4. package/templates/.claude/agents/dependency-manager.md +16 -1
  5. package/templates/.claude/agents/deployment-validator.md +16 -1
  6. package/templates/.claude/agents/git-operator.md +16 -1
  7. package/templates/.claude/agents/implementation-executor.md +16 -1
  8. package/templates/.claude/agents/lint-fixer.md +16 -1
  9. package/templates/.claude/agents/refactor-advisor.md +16 -1
  10. package/templates/.claude/commands/agent-creator.md +16 -1
  11. package/templates/.claude/commands/bug-fix.md +16 -1
  12. package/templates/.claude/commands/command-creator.md +17 -1
  13. package/templates/.claude/commands/docs-creator.md +17 -1
  14. package/templates/.claude/commands/docs-refactor.md +17 -1
  15. package/templates/.claude/commands/execute.md +17 -1
  16. package/templates/.claude/commands/git-all.md +16 -1
  17. package/templates/.claude/commands/git-session.md +17 -1
  18. package/templates/.claude/commands/git.md +17 -1
  19. package/templates/.claude/commands/lint-fix.md +17 -1
  20. package/templates/.claude/commands/lint-init.md +17 -1
  21. package/templates/.claude/commands/plan.md +17 -1
  22. package/templates/.claude/commands/prd.md +17 -1
  23. package/templates/.claude/commands/pre-deploy.md +17 -1
  24. package/templates/.claude/commands/refactor.md +17 -1
  25. package/templates/.claude/commands/version-update.md +17 -1
  26. package/templates/hono/CLAUDE.md +1 -0
  27. package/templates/nextjs/CLAUDE.md +12 -9
  28. package/templates/nextjs/docs/architecture.md +812 -0
  29. package/templates/npx/CLAUDE.md +1 -0
  30. package/templates/tanstack-start/CLAUDE.md +1 -0
@@ -0,0 +1,812 @@
1
+ # Architecture
2
+
3
+ > Next.js App Router Application Architecture
4
+
5
+ <instructions>
6
+ @library/nextjs/app-router.md
7
+ @library/nextjs/server-actions.md
8
+ @library/nextjs/middleware.md
9
+ @library/prisma/index.md
10
+ </instructions>
11
+
12
+ ---
13
+
14
+ <forbidden>
15
+
16
+ | Category | Forbidden |
17
+ |----------|-----------|
18
+ | **Routes** | Flat file routes (`app/users.tsx`), Pages Router (`pages/`) |
19
+ | **Route Export** | Named export (`export const Page`), incorrect file names (`Users.tsx`) |
20
+ | **API** | Pages Router API (`pages/api/`), API Routes overuse (use Server Actions) |
21
+ | **Layers** | Writing business logic directly in app/ folder |
22
+ | **Components** | Using client-only APIs without 'use client' |
23
+ | **Barrel Export** | Creating `actions/index.ts` (Tree Shaking fails) |
24
+
25
+ </forbidden>
26
+
27
+ ---
28
+
29
+ <required>
30
+
31
+ | Category | Required |
32
+ |----------|----------|
33
+ | **Route Structure** | Create folder per page (`app/users/page.tsx`) |
34
+ | **Route Export** | `export default function Page()` required |
35
+ | **Layer Structure** | app/ → Server Actions → lib/ → Database |
36
+ | **Route Group** | List pages → `(main)/`, Admin → `(admin)/` |
37
+ | **Page-specific Folders** | `_components/`, `_hooks/`, `_actions/` required (regardless of line count) |
38
+ | **Page Separation** | 100+ lines → `_components/`, 200+ lines → `_sections/` |
39
+ | **Server Actions** | Use Server Actions for mutations (`'use server'`) |
40
+ | **Validation** | Validate input with Zod schemas |
41
+ | **Metadata** | Export `generateMetadata` or `metadata` |
42
+ | **Error Handling** | `error.tsx` (route), `not-found.tsx` (404), `global-error.tsx` (global) |
43
+ | **Type Safety** | TypeScript strict, Prisma types |
44
+
45
+ </required>
46
+
47
+ ---
48
+
49
+ <system_overview>
50
+
51
+ ## System Overview
52
+
53
+ ```
54
+ ┌─────────────────────────────────────────────────────────────────┐
55
+ │ Client (Browser) │
56
+ │ ┌────────────────┐ ┌────────────────┐ ┌───────────────┐ │
57
+ │ │ Next.js Router │───▶│ TanStack Query │───▶│ React UI │ │
58
+ │ │ (Navigation) │◀───│ (Caching) │◀───│ (Components) │ │
59
+ │ └────────────────┘ └───────┬────────┘ └───────────────┘ │
60
+ └────────────────────────────────┼─────────────────────────────────┘
61
+
62
+ ┌─────────────────────────────────────────────────────────────────┐
63
+ │ Next.js Server │
64
+ │ ┌────────────────────────────────────────────────────────────┐ │
65
+ │ │ Server Components (default) │ │
66
+ │ │ app/[route]/page.tsx → Server-side rendering │ │
67
+ │ └────────────────────────────┬───────────────────────────────┘ │
68
+ │ ┌────────────────────────────▼───────────────────────────────┐ │
69
+ │ │ Server Actions │ │
70
+ │ │ 'use server' → DB access, Mutations, Revalidation │ │
71
+ │ └────────────────────────────┬───────────────────────────────┘ │
72
+ │ ┌────────────────────────────▼───────────────────────────────┐ │
73
+ │ │ Services Layer │ │
74
+ │ │ Zod Validation | Business Logic | Data Transformation │ │
75
+ │ └────────────────────────────┬───────────────────────────────┘ │
76
+ └───────────────────────────────┼──────────────────────────────────┘
77
+
78
+ ┌─────────────────────────────────────────────────────────────────┐
79
+ │ Database Layer │
80
+ │ ┌────────────────┐ ┌────────────────┐ ┌───────────────┐ │
81
+ │ │ Prisma Client │───▶│ PostgreSQL │ │ Redis │ │
82
+ │ └────────────────┘ └────────────────┘ └───────────────┘ │
83
+ └─────────────────────────────────────────────────────────────────┘
84
+ ```
85
+
86
+ </system_overview>
87
+
88
+ ---
89
+
90
+ <route_export_rule>
91
+
92
+ ## Route Export Rules
93
+
94
+ > ⚠️ **`export default` required**
95
+ >
96
+ > Next.js App Router requires all page/layout files to export components as **default export**.
97
+ >
98
+ > File names must follow Next.js conventions: `page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`
99
+
100
+ | ❌ Forbidden | ✅ Required |
101
+ |--------------|-------------|
102
+ | `app/users.tsx` | `app/users/page.tsx` |
103
+ | `export const Page = () => {}` | `export default function Page() {}` |
104
+ | `export default Users` (component name != file convention) | `export default function UsersPage() {}` |
105
+
106
+ ```typescript
107
+ // ❌ Forbidden: Flat file
108
+ // app/users.tsx
109
+ export default function Users() {
110
+ return <div>Users</div>
111
+ }
112
+
113
+ // ❌ Forbidden: named export
114
+ // app/users/page.tsx
115
+ export const Page = () => {
116
+ return <div>Users</div>
117
+ }
118
+
119
+ // ✅ Required: folder + page.tsx + default export
120
+ // app/users/page.tsx
121
+ export default function UsersPage() {
122
+ return <div>Users</div>
123
+ }
124
+ ```
125
+
126
+ </route_export_rule>
127
+
128
+ ---
129
+
130
+ <layers>
131
+
132
+ ## Layer Architecture
133
+
134
+ ### 1. Routes Layer (app/)
135
+
136
+ > ⚠️ **Create folder per page (required)**
137
+ >
138
+ > Every page MUST be created using **folder structure**. Flat file approach (`app/users.tsx`) is forbidden.
139
+ >
140
+ > **Reason:** To systematically manage page-specific resources like `_components/`, `_hooks/`, `_actions/`.
141
+ >
142
+ > | ❌ Forbidden | ✅ Required |
143
+ > |--------------|-------------|
144
+ > | `app/users.tsx` | `app/users/page.tsx` |
145
+ > | `app/posts.tsx` | `app/(main)/posts/page.tsx` |
146
+
147
+ ```
148
+ app/<route-name>/
149
+ ├── (main)/ # route group (list page, not in URL)
150
+ │ ├── page.tsx # page component
151
+ │ ├── _components/ # page-specific components (required)
152
+ │ ├── _hooks/ # page-specific hooks (required)
153
+ │ ├── _sections/ # UI section separation (200+ line pages)
154
+ │ └── _tabs/ # tab content separation
155
+ ├── new/ # creation page (outside route group)
156
+ │ └── page.tsx
157
+ ├── [id]/ # Dynamic segment
158
+ │ └── page.tsx
159
+ ├── layout.tsx # layout (shared across child routes)
160
+ ├── loading.tsx # loading UI (Suspense boundary)
161
+ ├── error.tsx # error UI (Error boundary)
162
+ └── _actions/ # page-specific Server Actions (required)
163
+ ```
164
+
165
+ **Required Rules:**
166
+ - Each page MUST have `_components/`, `_hooks/`, `_actions/` folders (regardless of line count)
167
+ - Custom Hooks MUST be separated into `_hooks/` folder regardless of page size
168
+ - Server Actions: global (`app/_actions/`) or page-specific (`[route]/_actions/`)
169
+ - Shared components → `components/ui/`, page-specific → `[route]/_components/`
170
+
171
+ | Pattern | Location | Purpose |
172
+ |---------|----------|---------|
173
+ | **Route Group** | `(main)/` | List page, not in URL |
174
+ | **Private Folder** | `_components/` | Ignored by routing system |
175
+ | **_sections/** | 200+ lines | Logical section separation |
176
+ | **_tabs/** | Tab UI | Tab content separation |
177
+ | **layout.tsx** | Layout | Shared UI for child routes |
178
+
179
+ #### Layout Routes Pattern
180
+
181
+ > ⚠️ **Compose layouts with layout.tsx**
182
+ >
183
+ > `layout.tsx` serves as the common layout for child routes.
184
+ > List pages should be wrapped in Route Group `(main)/`.
185
+ >
186
+ > | ❌ Forbidden | ✅ Required |
187
+ > |--------------|-------------|
188
+ > | `app/auth.tsx` | `app/(auth)/layout.tsx` + `app/(auth)/(main)/page.tsx` |
189
+
190
+ ```
191
+ app/
192
+ ├── (auth)/
193
+ │ ├── layout.tsx # layout (renders children)
194
+ │ ├── (main)/
195
+ │ │ └── page.tsx # /auth (main)
196
+ │ ├── login/
197
+ │ │ └── page.tsx # /auth/login
198
+ │ └── register/
199
+ │ └── page.tsx # /auth/register
200
+ ```
201
+
202
+ ```typescript
203
+ // ❌ Forbidden: flat structure without layout
204
+ // app/auth/page.tsx
205
+ export default function AuthPage() {
206
+ return <div>Auth</div>
207
+ }
208
+
209
+ // ✅ Required: wrap common UI with layout.tsx
210
+ // app/(auth)/layout.tsx
211
+ export default function AuthLayout({
212
+ children,
213
+ }: {
214
+ children: React.ReactNode
215
+ }) {
216
+ return (
217
+ <div className="auth-container">
218
+ <header>Auth Header</header>
219
+ {children}
220
+ </div>
221
+ )
222
+ }
223
+
224
+ // app/(auth)/(main)/page.tsx
225
+ export default function AuthMainPage() {
226
+ return <div>Auth Main</div>
227
+ }
228
+
229
+ // app/(auth)/login/page.tsx
230
+ export default function LoginPage() {
231
+ return <div>Login Form</div>
232
+ }
233
+ ```
234
+
235
+ ### 2. Server Actions Layer
236
+
237
+ ```
238
+ app/_actions/ # global (reusable)
239
+ ├── <action-name>.ts # one per file
240
+ └── types.ts # shared types
241
+
242
+ app/<route>/_actions/ # page-specific
243
+ └── <action-name>.ts
244
+ ```
245
+
246
+ > ⚠️ **Do NOT create `app/_actions/index.ts`**
247
+ >
248
+ > Do not create `index.ts` (barrel export) file in `app/_actions/` folder.
249
+ >
250
+ > **Problems:**
251
+ > 1. **Tree Shaking fails** - bundler includes unused functions
252
+ > 2. **Client bundle pollution** - server-only libraries like `prisma` get included in client bundle causing build errors
253
+ >
254
+ > ```typescript
255
+ > // ❌ Do NOT create app/_actions/index.ts
256
+ > export * from './get-users'
257
+ > export * from './create-post' // prisma import → client build failure
258
+ >
259
+ > // ✅ Import directly from individual files
260
+ > import { getUsers } from '@/app/_actions/get-users'
261
+ > import { createPost } from '@/app/_actions/create-post'
262
+ > ```
263
+
264
+ ### 3. Services Layer
265
+
266
+ ```
267
+ lib/<domain>/
268
+ ├── index.ts # entry point (re-export)
269
+ ├── schemas.ts # Zod schemas
270
+ ├── queries.ts # GET requests
271
+ └── mutations.ts # POST/PUT/PATCH
272
+ ```
273
+
274
+ ### 4. Database Layer
275
+
276
+ ```typescript
277
+ // lib/db/prisma.ts
278
+ import { PrismaClient } from '@prisma/client'
279
+
280
+ const globalForPrisma = globalThis as unknown as {
281
+ prisma: PrismaClient | undefined
282
+ }
283
+
284
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient()
285
+
286
+ if (process.env.NODE_ENV !== 'production') {
287
+ globalForPrisma.prisma = prisma
288
+ }
289
+ ```
290
+
291
+ </layers>
292
+
293
+ ---
294
+
295
+ <component_types>
296
+
297
+ ## Component Types
298
+
299
+ ### Server Components vs Client Components
300
+
301
+ | Item | Server Components | Client Components |
302
+ |------|------------------|-------------------|
303
+ | **Default** | ✅ Default (no declaration needed) | ❌ `'use client'` required |
304
+ | **Execution** | Server | Browser |
305
+ | **Data Fetching** | async/await direct usage | TanStack Query/SWR |
306
+ | **DB Access** | ✅ Possible | ❌ Impossible (use Server Actions) |
307
+ | **Browser API** | ❌ Impossible | ✅ Possible (window, localStorage, etc.) |
308
+ | **State Management** | ❌ Impossible | ✅ Possible (useState, useEffect, etc.) |
309
+ | **Event Handlers** | ❌ Impossible | ✅ Possible (onClick, onChange, etc.) |
310
+
311
+ ```typescript
312
+ // ✅ Server Component (default)
313
+ // app/users/page.tsx
314
+ import { prisma } from '@/lib/db/prisma'
315
+
316
+ export default async function UsersPage() {
317
+ // Direct DB query on server
318
+ const users = await prisma.user.findMany()
319
+
320
+ return (
321
+ <div>
322
+ {users.map((user) => (
323
+ <div key={user.id}>{user.name}</div>
324
+ ))}
325
+ </div>
326
+ )
327
+ }
328
+
329
+ // ✅ Client Component
330
+ // app/users/_components/user-list.tsx
331
+ 'use client'
332
+
333
+ import { useState } from 'react'
334
+
335
+ export default function UserList() {
336
+ const [count, setCount] = useState(0)
337
+
338
+ return (
339
+ <div>
340
+ <button onClick={() => setCount(count + 1)}>
341
+ Count: {count}
342
+ </button>
343
+ </div>
344
+ )
345
+ }
346
+ ```
347
+
348
+ ### Component Composition Strategy
349
+
350
+ ```
351
+ Page (Server Component)
352
+ ├─ Data fetching (async/await)
353
+ └─ Interactive UI (Client Component)
354
+ └─ State management, event handlers
355
+ ```
356
+
357
+ </component_types>
358
+
359
+ ---
360
+
361
+ <route_lifecycle>
362
+
363
+ ## Route Lifecycle
364
+
365
+ ### Loading & Error Handling
366
+
367
+ | File | Purpose | Required |
368
+ |------|---------|----------|
369
+ | **loading.tsx** | Loading UI (Suspense boundary) | Optional |
370
+ | **error.tsx** | Error UI (Error boundary) | ✅ |
371
+ | **not-found.tsx** | 404 UI | ✅ |
372
+ | **global-error.tsx** | Global error UI | Optional |
373
+
374
+ ```
375
+ app/
376
+ ├── layout.tsx
377
+ ├── loading.tsx # global loading
378
+ ├── error.tsx # global error
379
+ ├── not-found.tsx # global 404
380
+ ├── global-error.tsx # root error (catches layout.tsx errors too)
381
+ └── users/
382
+ ├── page.tsx
383
+ ├── loading.tsx # /users loading
384
+ └── error.tsx # /users error
385
+ ```
386
+
387
+ ### Code Patterns
388
+
389
+ ```typescript
390
+ // ✅ loading.tsx: loading UI
391
+ export default function Loading() {
392
+ return <div>Loading...</div>
393
+ }
394
+
395
+ // ✅ error.tsx: error UI (Client Component required)
396
+ 'use client'
397
+
398
+ export default function Error({
399
+ error,
400
+ reset,
401
+ }: {
402
+ error: Error & { digest?: string }
403
+ reset: () => void
404
+ }) {
405
+ return (
406
+ <div>
407
+ <h2>{error.message}</h2>
408
+ <button onClick={reset}>Retry</button>
409
+ </div>
410
+ )
411
+ }
412
+
413
+ // ✅ not-found.tsx: 404 UI
414
+ import Link from 'next/link'
415
+
416
+ export default function NotFound() {
417
+ return (
418
+ <div>
419
+ <h2>Not Found</h2>
420
+ <Link href="/">Home</Link>
421
+ </div>
422
+ )
423
+ }
424
+ ```
425
+
426
+ </route_lifecycle>
427
+
428
+ ---
429
+
430
+ <data_flow>
431
+
432
+ ## Data Flow
433
+
434
+ ### Query Flow (Read)
435
+
436
+ ```
437
+ Page (Server Component) → Prisma → Database
438
+
439
+ Auto caching (fetch cache)
440
+ ```
441
+
442
+ ```typescript
443
+ // ✅ Direct data fetching in Server Component
444
+ // app/users/page.tsx
445
+ import { prisma } from '@/lib/db/prisma'
446
+
447
+ export default async function UsersPage() {
448
+ const users = await prisma.user.findMany()
449
+
450
+ return (
451
+ <div>
452
+ {users.map((user) => (
453
+ <div key={user.id}>{user.name}</div>
454
+ ))}
455
+ </div>
456
+ )
457
+ }
458
+
459
+ // ✅ fetch with cache (default: 'force-cache')
460
+ async function getUsers() {
461
+ const res = await fetch('https://api.example.com/users', {
462
+ next: { revalidate: 3600 }, // 1 hour cache
463
+ })
464
+ return res.json()
465
+ }
466
+ ```
467
+
468
+ ### Mutation Flow (Write)
469
+
470
+ ```
471
+ Form (Client) → Server Action → Prisma → Database
472
+
473
+ revalidatePath/revalidateTag
474
+ ```
475
+
476
+ ```typescript
477
+ // ✅ Server Action
478
+ // app/_actions/create-user.ts
479
+ 'use server'
480
+
481
+ import { prisma } from '@/lib/db/prisma'
482
+ import { revalidatePath } from 'next/cache'
483
+ import { z } from 'zod'
484
+
485
+ const createUserSchema = z.object({
486
+ name: z.string().min(1),
487
+ email: z.email(),
488
+ })
489
+
490
+ export async function createUser(formData: FormData) {
491
+ // Validation
492
+ const parsed = createUserSchema.safeParse({
493
+ name: formData.get('name'),
494
+ email: formData.get('email'),
495
+ })
496
+
497
+ if (!parsed.success) {
498
+ return { error: parsed.error.errors }
499
+ }
500
+
501
+ // DB save
502
+ const user = await prisma.user.create({
503
+ data: parsed.data,
504
+ })
505
+
506
+ // Cache invalidation
507
+ revalidatePath('/users')
508
+
509
+ return { success: true, user }
510
+ }
511
+
512
+ // ✅ Usage in Client Component
513
+ // app/users/_components/user-form.tsx
514
+ 'use client'
515
+
516
+ import { createUser } from '@/app/_actions/create-user'
517
+
518
+ export default function UserForm() {
519
+ async function handleSubmit(formData: FormData) {
520
+ const result = await createUser(formData)
521
+
522
+ if (result.error) {
523
+ console.error(result.error)
524
+ } else {
525
+ console.log('User created:', result.user)
526
+ }
527
+ }
528
+
529
+ return (
530
+ <form action={handleSubmit}>
531
+ <input name="name" required />
532
+ <input name="email" type="email" required />
533
+ <button type="submit">Create</button>
534
+ </form>
535
+ )
536
+ }
537
+ ```
538
+
539
+ </data_flow>
540
+
541
+ ---
542
+
543
+ <server_actions_advanced>
544
+
545
+ ## Server Actions (Advanced)
546
+
547
+ ### Server Actions Patterns
548
+
549
+ | Pattern | Description | When to Use |
550
+ |---------|-------------|-------------|
551
+ | **Form Actions** | `<form action={...}>` | Form submission |
552
+ | **Programmatic** | `onClick={() => action()}` | Button click |
553
+ | **Progressive Enhancement** | Works without JS | Accessibility focus |
554
+
555
+ ```typescript
556
+ // ✅ Form Action (Progressive Enhancement)
557
+ // app/_actions/delete-user.ts
558
+ 'use server'
559
+
560
+ import { prisma } from '@/lib/db/prisma'
561
+ import { revalidatePath } from 'next/cache'
562
+ import { redirect } from 'next/navigation'
563
+
564
+ export async function deleteUser(formData: FormData) {
565
+ const id = formData.get('id') as string
566
+
567
+ await prisma.user.delete({
568
+ where: { id },
569
+ })
570
+
571
+ revalidatePath('/users')
572
+ redirect('/users')
573
+ }
574
+
575
+ // app/users/[id]/_components/delete-button.tsx
576
+ 'use client'
577
+
578
+ import { deleteUser } from '@/app/_actions/delete-user'
579
+
580
+ export default function DeleteButton({ id }: { id: string }) {
581
+ return (
582
+ <form action={deleteUser}>
583
+ <input type="hidden" name="id" value={id} />
584
+ <button type="submit">Delete</button>
585
+ </form>
586
+ )
587
+ }
588
+
589
+ // ✅ Programmatic Action
590
+ // app/users/_components/user-list.tsx
591
+ 'use client'
592
+
593
+ import { useState, useTransition } from 'react'
594
+ import { deleteUser } from '@/app/_actions/delete-user'
595
+
596
+ export default function UserList({ users }) {
597
+ const [isPending, startTransition] = useTransition()
598
+
599
+ function handleDelete(id: string) {
600
+ startTransition(async () => {
601
+ const formData = new FormData()
602
+ formData.append('id', id)
603
+ await deleteUser(formData)
604
+ })
605
+ }
606
+
607
+ return (
608
+ <div>
609
+ {users.map((user) => (
610
+ <div key={user.id}>
611
+ {user.name}
612
+ <button
613
+ onClick={() => handleDelete(user.id)}
614
+ disabled={isPending}
615
+ >
616
+ Delete
617
+ </button>
618
+ </div>
619
+ ))}
620
+ </div>
621
+ )
622
+ }
623
+ ```
624
+
625
+ ### Authentication Pattern
626
+
627
+ ```typescript
628
+ // ✅ lib/auth/session.ts
629
+ import { cookies } from 'next/headers'
630
+
631
+ export async function getSession() {
632
+ const cookieStore = await cookies()
633
+ const session = cookieStore.get('session')
634
+ // Session validation logic
635
+ return session
636
+ }
637
+
638
+ // ✅ Authentication check in Server Action
639
+ // app/_actions/create-post.ts
640
+ 'use server'
641
+
642
+ import { getSession } from '@/lib/auth/session'
643
+ import { prisma } from '@/lib/db/prisma'
644
+ import { redirect } from 'next/navigation'
645
+
646
+ export async function createPost(formData: FormData) {
647
+ const session = await getSession()
648
+
649
+ if (!session?.user) {
650
+ redirect('/login')
651
+ }
652
+
653
+ const post = await prisma.post.create({
654
+ data: {
655
+ title: formData.get('title') as string,
656
+ authorId: session.user.id,
657
+ },
658
+ })
659
+
660
+ return { success: true, post }
661
+ }
662
+ ```
663
+
664
+ </server_actions_advanced>
665
+
666
+ ---
667
+
668
+ <metadata>
669
+
670
+ ## Metadata
671
+
672
+ ### Static Metadata
673
+
674
+ ```typescript
675
+ // ✅ Static metadata export
676
+ // app/users/page.tsx
677
+ import { Metadata } from 'next'
678
+
679
+ export const metadata: Metadata = {
680
+ title: 'Users',
681
+ description: 'User list page',
682
+ }
683
+
684
+ export default function UsersPage() {
685
+ return <div>Users</div>
686
+ }
687
+ ```
688
+
689
+ ### Dynamic Metadata
690
+
691
+ ```typescript
692
+ // ✅ Dynamic metadata with generateMetadata
693
+ // app/users/[id]/page.tsx
694
+ import { Metadata } from 'next'
695
+ import { prisma } from '@/lib/db/prisma'
696
+
697
+ type Props = {
698
+ params: Promise<{ id: string }>
699
+ }
700
+
701
+ export async function generateMetadata({ params }: Props): Promise<Metadata> {
702
+ const { id } = await params
703
+ const user = await prisma.user.findUnique({ where: { id } })
704
+
705
+ return {
706
+ title: user?.name ?? 'User',
707
+ description: `Profile of ${user?.name}`,
708
+ }
709
+ }
710
+
711
+ export default async function UserPage({ params }: Props) {
712
+ const { id } = await params
713
+ const user = await prisma.user.findUnique({ where: { id } })
714
+
715
+ return <div>{user?.name}</div>
716
+ }
717
+ ```
718
+
719
+ </metadata>
720
+
721
+ ---
722
+
723
+ <caching>
724
+
725
+ ## Caching
726
+
727
+ ### fetch() Caching
728
+
729
+ | Option | Description |
730
+ |--------|-------------|
731
+ | `{ cache: 'force-cache' }` | Default, cache indefinitely |
732
+ | `{ cache: 'no-store' }` | No caching |
733
+ | `{ next: { revalidate: 3600 } }` | Revalidate every 3600 seconds |
734
+ | `{ next: { tags: ['users'] } }` | Tag-based invalidation |
735
+
736
+ ```typescript
737
+ // ✅ fetch with cache
738
+ async function getUsers() {
739
+ const res = await fetch('https://api.example.com/users', {
740
+ next: { revalidate: 3600, tags: ['users'] },
741
+ })
742
+ return res.json()
743
+ }
744
+
745
+ // ✅ Cache invalidation
746
+ // app/_actions/create-user.ts
747
+ 'use server'
748
+
749
+ import { revalidateTag, revalidatePath } from 'next/cache'
750
+
751
+ export async function createUser(data: any) {
752
+ // ...
753
+
754
+ // Tag-based invalidation
755
+ revalidateTag('users')
756
+
757
+ // Path-based invalidation
758
+ revalidatePath('/users')
759
+ }
760
+ ```
761
+
762
+ ### unstable_cache (Prisma, etc.)
763
+
764
+ ```typescript
765
+ // ✅ Prisma query caching
766
+ import { unstable_cache } from 'next/cache'
767
+ import { prisma } from '@/lib/db/prisma'
768
+
769
+ const getUsers = unstable_cache(
770
+ async () => prisma.user.findMany(),
771
+ ['users'],
772
+ {
773
+ revalidate: 3600,
774
+ tags: ['users'],
775
+ }
776
+ )
777
+
778
+ export default async function UsersPage() {
779
+ const users = await getUsers()
780
+ return <div>{/* ... */}</div>
781
+ }
782
+ ```
783
+
784
+ </caching>
785
+
786
+ ---
787
+
788
+ <tech_stack>
789
+
790
+ ## Technology Stack
791
+
792
+ | Layer | Technology | Version |
793
+ |-------|------------|---------|
794
+ | Framework | Next.js | 15+ |
795
+ | Router | App Router | - |
796
+ | Data | TanStack Query | 5.x |
797
+ | ORM | Prisma | 7.x |
798
+ | Validation | Zod | 4.x |
799
+ | Database | PostgreSQL | - |
800
+ | UI | React 19+ | - |
801
+
802
+ </tech_stack>
803
+
804
+ ---
805
+
806
+ ## Sources
807
+
808
+ - [Next.js App Router Documentation](https://nextjs.org/docs/app)
809
+ - [Next.js Project Structure Guide](https://nextjs.org/docs/app/getting-started/project-structure)
810
+ - [Next.js 15 App Router Best Practices (Medium)](https://medium.com/better-dev-nextjs-react/inside-the-app-router-best-practices-for-next-js-file-and-directory-structure-2025-edition-ed6bc14a8da3)
811
+ - [Mastering Next.js App Router (Medium)](https://thiraphat-ps-dev.medium.com/mastering-next-js-app-router-best-practices-for-structuring-your-application-3f8cf0c76580)
812
+ - [Modern Full Stack Architecture with Next.js 15+](https://softwaremill.com/modern-full-stack-application-architecture-using-next-js-15/)