agentic-team-templates 0.3.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 (103) hide show
  1. package/README.md +280 -0
  2. package/bin/cli.js +5 -0
  3. package/package.json +47 -0
  4. package/src/index.js +521 -0
  5. package/templates/_shared/code-quality.md +162 -0
  6. package/templates/_shared/communication.md +114 -0
  7. package/templates/_shared/core-principles.md +62 -0
  8. package/templates/_shared/git-workflow.md +165 -0
  9. package/templates/_shared/security-fundamentals.md +173 -0
  10. package/templates/blockchain/.cursorrules/defi-patterns.md +520 -0
  11. package/templates/blockchain/.cursorrules/gas-optimization.md +339 -0
  12. package/templates/blockchain/.cursorrules/overview.md +130 -0
  13. package/templates/blockchain/.cursorrules/security.md +318 -0
  14. package/templates/blockchain/.cursorrules/smart-contracts.md +364 -0
  15. package/templates/blockchain/.cursorrules/testing.md +415 -0
  16. package/templates/blockchain/.cursorrules/web3-integration.md +538 -0
  17. package/templates/blockchain/CLAUDE.md +389 -0
  18. package/templates/cli-tools/.cursorrules/architecture.md +412 -0
  19. package/templates/cli-tools/.cursorrules/arguments.md +406 -0
  20. package/templates/cli-tools/.cursorrules/distribution.md +546 -0
  21. package/templates/cli-tools/.cursorrules/error-handling.md +455 -0
  22. package/templates/cli-tools/.cursorrules/overview.md +136 -0
  23. package/templates/cli-tools/.cursorrules/testing.md +537 -0
  24. package/templates/cli-tools/.cursorrules/user-experience.md +545 -0
  25. package/templates/cli-tools/CLAUDE.md +356 -0
  26. package/templates/data-engineering/.cursorrules/data-modeling.md +367 -0
  27. package/templates/data-engineering/.cursorrules/data-quality.md +455 -0
  28. package/templates/data-engineering/.cursorrules/overview.md +85 -0
  29. package/templates/data-engineering/.cursorrules/performance.md +339 -0
  30. package/templates/data-engineering/.cursorrules/pipeline-design.md +280 -0
  31. package/templates/data-engineering/.cursorrules/security.md +460 -0
  32. package/templates/data-engineering/.cursorrules/testing.md +452 -0
  33. package/templates/data-engineering/CLAUDE.md +974 -0
  34. package/templates/devops-sre/.cursorrules/capacity-planning.md +653 -0
  35. package/templates/devops-sre/.cursorrules/change-management.md +584 -0
  36. package/templates/devops-sre/.cursorrules/chaos-engineering.md +651 -0
  37. package/templates/devops-sre/.cursorrules/disaster-recovery.md +641 -0
  38. package/templates/devops-sre/.cursorrules/incident-management.md +565 -0
  39. package/templates/devops-sre/.cursorrules/observability.md +714 -0
  40. package/templates/devops-sre/.cursorrules/overview.md +230 -0
  41. package/templates/devops-sre/.cursorrules/postmortems.md +588 -0
  42. package/templates/devops-sre/.cursorrules/runbooks.md +760 -0
  43. package/templates/devops-sre/.cursorrules/slo-sli.md +617 -0
  44. package/templates/devops-sre/.cursorrules/toil-reduction.md +567 -0
  45. package/templates/devops-sre/CLAUDE.md +1007 -0
  46. package/templates/documentation/.cursorrules/adr.md +277 -0
  47. package/templates/documentation/.cursorrules/api-documentation.md +411 -0
  48. package/templates/documentation/.cursorrules/code-comments.md +253 -0
  49. package/templates/documentation/.cursorrules/maintenance.md +260 -0
  50. package/templates/documentation/.cursorrules/overview.md +82 -0
  51. package/templates/documentation/.cursorrules/readme-standards.md +306 -0
  52. package/templates/documentation/CLAUDE.md +120 -0
  53. package/templates/fullstack/.cursorrules/api-contracts.md +331 -0
  54. package/templates/fullstack/.cursorrules/architecture.md +298 -0
  55. package/templates/fullstack/.cursorrules/overview.md +109 -0
  56. package/templates/fullstack/.cursorrules/shared-types.md +348 -0
  57. package/templates/fullstack/.cursorrules/testing.md +386 -0
  58. package/templates/fullstack/CLAUDE.md +349 -0
  59. package/templates/ml-ai/.cursorrules/data-engineering.md +483 -0
  60. package/templates/ml-ai/.cursorrules/deployment.md +601 -0
  61. package/templates/ml-ai/.cursorrules/model-development.md +538 -0
  62. package/templates/ml-ai/.cursorrules/monitoring.md +658 -0
  63. package/templates/ml-ai/.cursorrules/overview.md +131 -0
  64. package/templates/ml-ai/.cursorrules/security.md +637 -0
  65. package/templates/ml-ai/.cursorrules/testing.md +678 -0
  66. package/templates/ml-ai/CLAUDE.md +1136 -0
  67. package/templates/mobile/.cursorrules/navigation.md +246 -0
  68. package/templates/mobile/.cursorrules/offline-first.md +302 -0
  69. package/templates/mobile/.cursorrules/overview.md +71 -0
  70. package/templates/mobile/.cursorrules/performance.md +345 -0
  71. package/templates/mobile/.cursorrules/testing.md +339 -0
  72. package/templates/mobile/CLAUDE.md +233 -0
  73. package/templates/platform-engineering/.cursorrules/ci-cd.md +778 -0
  74. package/templates/platform-engineering/.cursorrules/developer-experience.md +632 -0
  75. package/templates/platform-engineering/.cursorrules/infrastructure-as-code.md +600 -0
  76. package/templates/platform-engineering/.cursorrules/kubernetes.md +710 -0
  77. package/templates/platform-engineering/.cursorrules/observability.md +747 -0
  78. package/templates/platform-engineering/.cursorrules/overview.md +215 -0
  79. package/templates/platform-engineering/.cursorrules/security.md +855 -0
  80. package/templates/platform-engineering/.cursorrules/testing.md +878 -0
  81. package/templates/platform-engineering/CLAUDE.md +850 -0
  82. package/templates/utility-agent/.cursorrules/action-control.md +284 -0
  83. package/templates/utility-agent/.cursorrules/context-management.md +186 -0
  84. package/templates/utility-agent/.cursorrules/hallucination-prevention.md +253 -0
  85. package/templates/utility-agent/.cursorrules/overview.md +78 -0
  86. package/templates/utility-agent/.cursorrules/token-optimization.md +369 -0
  87. package/templates/utility-agent/CLAUDE.md +513 -0
  88. package/templates/web-backend/.cursorrules/api-design.md +255 -0
  89. package/templates/web-backend/.cursorrules/authentication.md +309 -0
  90. package/templates/web-backend/.cursorrules/database-patterns.md +298 -0
  91. package/templates/web-backend/.cursorrules/error-handling.md +366 -0
  92. package/templates/web-backend/.cursorrules/overview.md +69 -0
  93. package/templates/web-backend/.cursorrules/security.md +358 -0
  94. package/templates/web-backend/.cursorrules/testing.md +395 -0
  95. package/templates/web-backend/CLAUDE.md +366 -0
  96. package/templates/web-frontend/.cursorrules/accessibility.md +296 -0
  97. package/templates/web-frontend/.cursorrules/component-patterns.md +204 -0
  98. package/templates/web-frontend/.cursorrules/overview.md +72 -0
  99. package/templates/web-frontend/.cursorrules/performance.md +325 -0
  100. package/templates/web-frontend/.cursorrules/state-management.md +227 -0
  101. package/templates/web-frontend/.cursorrules/styling.md +271 -0
  102. package/templates/web-frontend/.cursorrules/testing.md +311 -0
  103. package/templates/web-frontend/CLAUDE.md +399 -0
@@ -0,0 +1,331 @@
1
+ # API Contracts
2
+
3
+ Defining and maintaining contracts between frontend and backend.
4
+
5
+ ## Why API Contracts Matter
6
+
7
+ - **Type Safety**: Catch mismatches at compile time
8
+ - **Documentation**: Self-documenting API surface
9
+ - **Consistency**: Single source of truth
10
+ - **Parallel Development**: Frontend and backend can work independently
11
+
12
+ ## Schema-Driven Development
13
+
14
+ ### Define Schemas First
15
+
16
+ ```ts
17
+ // shared/schemas/user.ts
18
+ import { z } from 'zod';
19
+
20
+ // Request schemas
21
+ export const CreateUserRequestSchema = z.object({
22
+ email: z.string().email(),
23
+ name: z.string().min(1).max(100),
24
+ role: z.enum(['user', 'admin']).default('user'),
25
+ });
26
+
27
+ export const UpdateUserRequestSchema = CreateUserRequestSchema.partial();
28
+
29
+ // Response schemas
30
+ export const UserResponseSchema = z.object({
31
+ id: z.string(),
32
+ email: z.string(),
33
+ name: z.string(),
34
+ role: z.enum(['user', 'admin']),
35
+ createdAt: z.string().datetime(),
36
+ });
37
+
38
+ export const UsersListResponseSchema = z.object({
39
+ data: z.array(UserResponseSchema),
40
+ pagination: z.object({
41
+ page: z.number(),
42
+ limit: z.number(),
43
+ total: z.number(),
44
+ }),
45
+ });
46
+
47
+ // Infer types from schemas
48
+ export type CreateUserRequest = z.infer<typeof CreateUserRequestSchema>;
49
+ export type UpdateUserRequest = z.infer<typeof UpdateUserRequestSchema>;
50
+ export type UserResponse = z.infer<typeof UserResponseSchema>;
51
+ export type UsersListResponse = z.infer<typeof UsersListResponseSchema>;
52
+ ```
53
+
54
+ ### Use on Backend
55
+
56
+ ```ts
57
+ // api/routes/users.ts
58
+ import { CreateUserRequestSchema, UserResponseSchema } from '@shared/schemas/user';
59
+
60
+ app.post('/users', async (req, res) => {
61
+ // Validate input
62
+ const parsed = CreateUserRequestSchema.safeParse(req.body);
63
+ if (!parsed.success) {
64
+ return res.status(422).json({ error: parsed.error });
65
+ }
66
+
67
+ const user = await createUser(parsed.data);
68
+
69
+ // Validate output (optional but good for catching bugs)
70
+ const response = UserResponseSchema.parse(user);
71
+ res.status(201).json({ data: response });
72
+ });
73
+ ```
74
+
75
+ ### Use on Frontend
76
+
77
+ ```ts
78
+ // lib/api/users.ts
79
+ import {
80
+ CreateUserRequest,
81
+ UserResponse,
82
+ UsersListResponse,
83
+ CreateUserRequestSchema,
84
+ } from '@shared/schemas/user';
85
+
86
+ export const usersApi = {
87
+ async create(data: CreateUserRequest): Promise<UserResponse> {
88
+ // Validate before sending (catches client-side errors early)
89
+ const validated = CreateUserRequestSchema.parse(data);
90
+
91
+ const response = await fetch('/api/users', {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify(validated),
95
+ });
96
+
97
+ if (!response.ok) {
98
+ throw new ApiError(response);
99
+ }
100
+
101
+ const json = await response.json();
102
+ return json.data;
103
+ },
104
+
105
+ async list(page = 1, limit = 20): Promise<UsersListResponse> {
106
+ const response = await fetch(`/api/users?page=${page}&limit=${limit}`);
107
+ if (!response.ok) throw new ApiError(response);
108
+ return response.json();
109
+ },
110
+ };
111
+ ```
112
+
113
+ ## OpenAPI / Swagger
114
+
115
+ ### Generate from Code
116
+
117
+ ```ts
118
+ // Using Zod to OpenAPI
119
+ import { OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
120
+
121
+ const registry = new OpenAPIRegistry();
122
+
123
+ registry.registerPath({
124
+ method: 'post',
125
+ path: '/users',
126
+ summary: 'Create a new user',
127
+ request: {
128
+ body: {
129
+ content: { 'application/json': { schema: CreateUserRequestSchema } },
130
+ },
131
+ },
132
+ responses: {
133
+ 201: {
134
+ description: 'User created',
135
+ content: { 'application/json': { schema: UserResponseSchema } },
136
+ },
137
+ 422: { description: 'Validation error' },
138
+ },
139
+ });
140
+
141
+ const generator = new OpenApiGeneratorV3(registry.definitions);
142
+ export const openApiDoc = generator.generateDocument({
143
+ info: { title: 'Users API', version: '1.0.0' },
144
+ });
145
+ ```
146
+
147
+ ### Manual OpenAPI Spec
148
+
149
+ ```yaml
150
+ # openapi.yaml
151
+ openapi: 3.0.0
152
+ info:
153
+ title: Users API
154
+ version: 1.0.0
155
+
156
+ paths:
157
+ /users:
158
+ post:
159
+ summary: Create a new user
160
+ requestBody:
161
+ required: true
162
+ content:
163
+ application/json:
164
+ schema:
165
+ $ref: '#/components/schemas/CreateUserRequest'
166
+ responses:
167
+ '201':
168
+ description: User created
169
+ content:
170
+ application/json:
171
+ schema:
172
+ $ref: '#/components/schemas/UserResponse'
173
+
174
+ components:
175
+ schemas:
176
+ CreateUserRequest:
177
+ type: object
178
+ required: [email, name]
179
+ properties:
180
+ email:
181
+ type: string
182
+ format: email
183
+ name:
184
+ type: string
185
+ minLength: 1
186
+ maxLength: 100
187
+ ```
188
+
189
+ ## Type-Safe API Clients
190
+
191
+ ### Using tRPC
192
+
193
+ ```ts
194
+ // server/trpc.ts
195
+ import { initTRPC } from '@trpc/server';
196
+ import { z } from 'zod';
197
+
198
+ const t = initTRPC.create();
199
+
200
+ export const appRouter = t.router({
201
+ user: t.router({
202
+ list: t.procedure
203
+ .input(z.object({ page: z.number().default(1) }))
204
+ .query(async ({ input }) => {
205
+ return db.user.findMany({ skip: (input.page - 1) * 20, take: 20 });
206
+ }),
207
+
208
+ create: t.procedure
209
+ .input(CreateUserRequestSchema)
210
+ .mutation(async ({ input }) => {
211
+ return db.user.create({ data: input });
212
+ }),
213
+ }),
214
+ });
215
+
216
+ export type AppRouter = typeof appRouter;
217
+
218
+ // client/trpc.ts
219
+ import { createTRPCReact } from '@trpc/react-query';
220
+ import type { AppRouter } from '../server/trpc';
221
+
222
+ export const trpc = createTRPCReact<AppRouter>();
223
+
224
+ // Usage in component
225
+ const users = trpc.user.list.useQuery({ page: 1 });
226
+ const createUser = trpc.user.create.useMutation();
227
+ ```
228
+
229
+ ### Type-Safe Fetch Wrapper
230
+
231
+ ```ts
232
+ // lib/api/client.ts
233
+ type ApiResponse<T> = { data: T } | { error: { code: string; message: string } };
234
+
235
+ async function apiRequest<T>(
236
+ path: string,
237
+ options?: RequestInit
238
+ ): Promise<T> {
239
+ const response = await fetch(`/api${path}`, {
240
+ ...options,
241
+ headers: {
242
+ 'Content-Type': 'application/json',
243
+ ...options?.headers,
244
+ },
245
+ });
246
+
247
+ const json: ApiResponse<T> = await response.json();
248
+
249
+ if ('error' in json) {
250
+ throw new ApiError(json.error.code, json.error.message);
251
+ }
252
+
253
+ return json.data;
254
+ }
255
+
256
+ // Typed endpoints
257
+ export const api = {
258
+ users: {
259
+ list: () => apiRequest<UsersListResponse>('/users'),
260
+ get: (id: string) => apiRequest<UserResponse>(`/users/${id}`),
261
+ create: (data: CreateUserRequest) =>
262
+ apiRequest<UserResponse>('/users', {
263
+ method: 'POST',
264
+ body: JSON.stringify(data),
265
+ }),
266
+ },
267
+ };
268
+ ```
269
+
270
+ ## Versioning
271
+
272
+ ### URL Versioning
273
+
274
+ ```
275
+ /api/v1/users
276
+ /api/v2/users
277
+ ```
278
+
279
+ ### Backwards Compatibility
280
+
281
+ When evolving APIs:
282
+
283
+ ```ts
284
+ // v1 response
285
+ interface UserV1 {
286
+ id: string;
287
+ name: string; // Full name
288
+ }
289
+
290
+ // v2 response (split name)
291
+ interface UserV2 {
292
+ id: string;
293
+ firstName: string;
294
+ lastName: string;
295
+ name: string; // Keep for backwards compatibility
296
+ }
297
+
298
+ // Transformer for v1 clients
299
+ const toV1Response = (user: UserV2): UserV1 => ({
300
+ id: user.id,
301
+ name: user.name,
302
+ });
303
+ ```
304
+
305
+ ## Contract Testing
306
+
307
+ ```ts
308
+ // Verify backend implements contract correctly
309
+ describe('Users API Contract', () => {
310
+ it('POST /users returns valid UserResponse', async () => {
311
+ const response = await request(app)
312
+ .post('/users')
313
+ .send({ email: 'test@example.com', name: 'Test' });
314
+
315
+ expect(response.status).toBe(201);
316
+
317
+ // Validate against schema
318
+ const result = UserResponseSchema.safeParse(response.body.data);
319
+ expect(result.success).toBe(true);
320
+ });
321
+
322
+ it('GET /users returns valid UsersListResponse', async () => {
323
+ const response = await request(app).get('/users');
324
+
325
+ expect(response.status).toBe(200);
326
+
327
+ const result = UsersListResponseSchema.safeParse(response.body);
328
+ expect(result.success).toBe(true);
329
+ });
330
+ });
331
+ ```
@@ -0,0 +1,298 @@
1
+ # Fullstack Architecture
2
+
3
+ Architectural patterns for full-stack applications.
4
+
5
+ ## Architectural Layers
6
+
7
+ ### Presentation Layer (Frontend)
8
+ - Components and pages
9
+ - State management
10
+ - User interactions
11
+ - Client-side routing
12
+
13
+ ### Application Layer (API)
14
+ - Route handlers
15
+ - Request validation
16
+ - Authentication/authorization
17
+ - Response formatting
18
+
19
+ ### Domain Layer (Business Logic)
20
+ - Business rules
21
+ - Domain models
22
+ - Use cases/services
23
+ - Pure functions
24
+
25
+ ### Infrastructure Layer
26
+ - Database access
27
+ - External API clients
28
+ - File storage
29
+ - Caching
30
+
31
+ ## Layer Boundaries
32
+
33
+ ### Dependency Direction
34
+
35
+ Dependencies should point inward (toward domain):
36
+
37
+ ```
38
+ Presentation → Application → Domain ← Infrastructure
39
+ ```
40
+
41
+ ### Example
42
+
43
+ ```ts
44
+ // Domain layer (no dependencies on outer layers)
45
+ // domain/user.ts
46
+ export interface User {
47
+ id: string;
48
+ email: string;
49
+ name: string;
50
+ }
51
+
52
+ export interface UserRepository {
53
+ findById(id: string): Promise<User | null>;
54
+ create(data: CreateUserInput): Promise<User>;
55
+ }
56
+
57
+ export const createUser = async (
58
+ repo: UserRepository,
59
+ input: CreateUserInput
60
+ ): Promise<User> => {
61
+ // Business logic here
62
+ const validated = validateUserInput(input);
63
+ return repo.create(validated);
64
+ };
65
+
66
+ // Infrastructure layer (implements domain interfaces)
67
+ // infrastructure/userRepository.ts
68
+ import { db } from './db';
69
+ import { UserRepository } from '../domain/user';
70
+
71
+ export const prismaUserRepository: UserRepository = {
72
+ findById: (id) => db.user.findUnique({ where: { id } }),
73
+ create: (data) => db.user.create({ data }),
74
+ };
75
+
76
+ // Application layer (coordinates domain and infrastructure)
77
+ // api/users.ts
78
+ import { createUser } from '../domain/user';
79
+ import { prismaUserRepository } from '../infrastructure/userRepository';
80
+
81
+ export const handleCreateUser = async (req, res) => {
82
+ const result = await createUser(prismaUserRepository, req.body);
83
+ res.json({ data: result });
84
+ };
85
+ ```
86
+
87
+ ## Server Components vs Client Components
88
+
89
+ ### Server Components (Default)
90
+ - Render on server
91
+ - Can access database directly
92
+ - No interactivity
93
+ - No client-side state
94
+
95
+ ```tsx
96
+ // app/users/page.tsx (Server Component)
97
+ import { db } from '@/lib/server/db';
98
+
99
+ export default async function UsersPage() {
100
+ const users = await db.user.findMany(); // Direct DB access
101
+
102
+ return (
103
+ <ul>
104
+ {users.map(user => (
105
+ <li key={user.id}>{user.name}</li>
106
+ ))}
107
+ </ul>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ### Client Components
113
+ - Run in browser
114
+ - Handle interactivity
115
+ - Manage client state
116
+ - Use hooks
117
+
118
+ ```tsx
119
+ // components/UserForm.tsx
120
+ 'use client';
121
+
122
+ import { useState } from 'react';
123
+
124
+ export function UserForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
125
+ const [name, setName] = useState('');
126
+
127
+ return (
128
+ <form onSubmit={(e) => {
129
+ e.preventDefault();
130
+ onSubmit({ name });
131
+ }}>
132
+ <input value={name} onChange={(e) => setName(e.target.value)} />
133
+ <button type="submit">Submit</button>
134
+ </form>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### Composition Pattern
140
+
141
+ ```tsx
142
+ // Server component fetches data
143
+ // app/users/[id]/page.tsx
144
+ import { db } from '@/lib/server/db';
145
+ import { UserEditor } from '@/components/UserEditor';
146
+
147
+ export default async function UserPage({ params }: { params: { id: string } }) {
148
+ const user = await db.user.findUnique({ where: { id: params.id } });
149
+
150
+ if (!user) return <NotFound />;
151
+
152
+ return (
153
+ <div>
154
+ <h1>Edit User</h1>
155
+ <UserEditor user={user} /> {/* Client component for interactivity */}
156
+ </div>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ## API Patterns
162
+
163
+ ### Server Actions (Full-Stack Frameworks)
164
+
165
+ ```tsx
166
+ // app/actions/users.ts
167
+ 'use server';
168
+
169
+ import { db } from '@/lib/server/db';
170
+ import { revalidatePath } from 'next/cache';
171
+
172
+ export async function createUser(formData: FormData) {
173
+ const name = formData.get('name') as string;
174
+ const email = formData.get('email') as string;
175
+
176
+ await db.user.create({ data: { name, email } });
177
+
178
+ revalidatePath('/users');
179
+ }
180
+
181
+ // Usage in component
182
+ <form action={createUser}>
183
+ <input name="name" />
184
+ <input name="email" type="email" />
185
+ <button type="submit">Create</button>
186
+ </form>
187
+ ```
188
+
189
+ ### API Routes
190
+
191
+ ```ts
192
+ // app/api/users/route.ts
193
+ import { db } from '@/lib/server/db';
194
+ import { NextResponse } from 'next/server';
195
+
196
+ export async function GET() {
197
+ const users = await db.user.findMany();
198
+ return NextResponse.json({ data: users });
199
+ }
200
+
201
+ export async function POST(request: Request) {
202
+ const body = await request.json();
203
+ const user = await db.user.create({ data: body });
204
+ return NextResponse.json({ data: user }, { status: 201 });
205
+ }
206
+ ```
207
+
208
+ ## State Synchronization
209
+
210
+ ### Optimistic Updates
211
+
212
+ ```tsx
213
+ 'use client';
214
+
215
+ import { useOptimistic } from 'react';
216
+
217
+ export function TodoList({ todos, addTodo }: TodoListProps) {
218
+ const [optimisticTodos, addOptimisticTodo] = useOptimistic(
219
+ todos,
220
+ (state, newTodo: Todo) => [...state, newTodo]
221
+ );
222
+
223
+ async function handleAdd(formData: FormData) {
224
+ const title = formData.get('title') as string;
225
+
226
+ // Optimistically add to UI
227
+ addOptimisticTodo({ id: 'temp', title, completed: false });
228
+
229
+ // Then persist to server
230
+ await addTodo(formData);
231
+ }
232
+
233
+ return (
234
+ <form action={handleAdd}>
235
+ <input name="title" />
236
+ <button type="submit">Add</button>
237
+ <ul>
238
+ {optimisticTodos.map(todo => (
239
+ <li key={todo.id}>{todo.title}</li>
240
+ ))}
241
+ </ul>
242
+ </form>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ### Cache Invalidation
248
+
249
+ ```ts
250
+ // After mutation, invalidate relevant caches
251
+ import { revalidatePath, revalidateTag } from 'next/cache';
252
+
253
+ export async function updateUser(id: string, data: UpdateUserInput) {
254
+ await db.user.update({ where: { id }, data });
255
+
256
+ // Invalidate specific path
257
+ revalidatePath(`/users/${id}`);
258
+
259
+ // Or invalidate by tag
260
+ revalidateTag('users');
261
+ }
262
+ ```
263
+
264
+ ## Error Handling Across Stack
265
+
266
+ ```tsx
267
+ // Server-side error
268
+ // app/api/users/[id]/route.ts
269
+ export async function GET(request: Request, { params }: { params: { id: string } }) {
270
+ const user = await db.user.findUnique({ where: { id: params.id } });
271
+
272
+ if (!user) {
273
+ return NextResponse.json(
274
+ { error: { code: 'NOT_FOUND', message: 'User not found' } },
275
+ { status: 404 }
276
+ );
277
+ }
278
+
279
+ return NextResponse.json({ data: user });
280
+ }
281
+
282
+ // Client-side handling
283
+ // components/UserProfile.tsx
284
+ 'use client';
285
+
286
+ export function UserProfile({ userId }: { userId: string }) {
287
+ const { data, error, isLoading } = useQuery(['user', userId], () =>
288
+ fetch(`/api/users/${userId}`).then(res => {
289
+ if (!res.ok) throw new Error('Failed to fetch user');
290
+ return res.json();
291
+ })
292
+ );
293
+
294
+ if (isLoading) return <Skeleton />;
295
+ if (error) return <ErrorMessage error={error} />;
296
+ return <UserCard user={data.data} />;
297
+ }
298
+ ```