@mr.dj2u/knowledge 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 (83) hide show
  1. package/dist/content/checklists/ship-test-loop.md +16 -0
  2. package/dist/content/checklists/unified-agent-bundle-validation.md +8 -0
  3. package/dist/content/examples/ship-test-loop.md +13 -0
  4. package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -0
  5. package/dist/content/guides/animation-performance.md +30 -0
  6. package/dist/content/guides/post-create-onboarding.md +113 -0
  7. package/dist/content/patterns/api/api-routes.md +314 -0
  8. package/dist/content/patterns/api/error-handling.md +311 -0
  9. package/dist/content/patterns/database/drizzle-schema.md +280 -0
  10. package/dist/content/patterns/database/migrations.md +365 -0
  11. package/dist/content/patterns/database/query-organization.md +537 -0
  12. package/dist/content/patterns/database/relations.md +450 -0
  13. package/dist/content/patterns/deployment/build-configuration.md +452 -0
  14. package/dist/content/patterns/deployment/ci-cd-patterns.md +448 -0
  15. package/dist/content/patterns/deployment/environment-config.md +380 -0
  16. package/dist/content/patterns/deployment/hosting-setup.md +425 -0
  17. package/dist/content/patterns/project/configuration-patterns.md +459 -0
  18. package/dist/content/patterns/project/documentation-org.md +506 -0
  19. package/dist/content/patterns/project/folder-structure.md +398 -0
  20. package/dist/content/patterns/project/library-exports.md +465 -0
  21. package/dist/content/patterns/project/monorepo-structure.md +500 -0
  22. package/dist/content/patterns/routing/dynamic-routes.md +221 -0
  23. package/dist/content/patterns/routing/file-based-routing.md +186 -0
  24. package/dist/content/patterns/routing/route-groups.md +429 -0
  25. package/dist/content/patterns/state/persistence-middleware.md +521 -0
  26. package/dist/content/patterns/state/selector-hooks.md +538 -0
  27. package/dist/content/patterns/state/store-organization.md +539 -0
  28. package/dist/content/patterns/state/zustand-patterns.md +348 -0
  29. package/dist/content/patterns/styling/component-styling.md +468 -0
  30. package/dist/content/patterns/styling/responsive-patterns.md +398 -0
  31. package/dist/content/patterns/styling/theme-configuration.md +426 -0
  32. package/dist/content/patterns/styling/uniwind-setup.md +412 -0
  33. package/dist/content/prompts/continue-development.md +27 -0
  34. package/dist/content/prompts/create-expo-super-stack.md +29 -0
  35. package/dist/content/prompts/fix-seo.md +29 -0
  36. package/dist/content/prompts/onboard-new-expo-app.md +11 -0
  37. package/dist/content/prompts/prepare-deploy.md +29 -0
  38. package/dist/content/prompts/project-research-plan.md +29 -0
  39. package/dist/content/prompts/review-expo-project.md +29 -0
  40. package/dist/content/prompts/run-doctor.md +30 -0
  41. package/dist/content/prompts/ship-test-loop.md +24 -0
  42. package/dist/content/reference/create-expo-stack-uniwind.md +29 -0
  43. package/dist/content/reference/doctor-dogfood.md +42 -0
  44. package/dist/content/reference/mcp-sdk-transport.md +30 -0
  45. package/dist/content/reference/package-ci-patterns.md +24 -0
  46. package/dist/content/reference/reference-repo-evacuation.md +31 -0
  47. package/dist/content/resource-index.json +67 -0
  48. package/dist/content/rules/app-folder-architecture.md +13 -0
  49. package/dist/content/rules/env-hygiene.md +9 -0
  50. package/dist/content/rules/seo-metadata.md +7 -0
  51. package/dist/content/rules/ssr-safety.md +9 -0
  52. package/dist/content/skills/api-routes.md +33 -0
  53. package/dist/content/skills/continue-development.md +31 -0
  54. package/dist/content/skills/debugging.md +31 -0
  55. package/dist/content/skills/deployment.md +32 -0
  56. package/dist/content/skills/dev-server-management.md +31 -0
  57. package/dist/content/skills/env-vars.md +32 -0
  58. package/dist/content/skills/expo-router-architecture.md +33 -0
  59. package/dist/content/skills/expo-ssr-safety.md +32 -0
  60. package/dist/content/skills/plugin-creation.md +41 -0
  61. package/dist/content/skills/production-server-patterns.md +31 -0
  62. package/dist/content/skills/project-onboarding.md +31 -0
  63. package/dist/content/skills/research-plan-intake.md +31 -0
  64. package/dist/content/skills/seo-metadata.md +31 -0
  65. package/dist/content/skills/super-stack-startup.md +31 -0
  66. package/dist/content/skills/uniwind-theming.md +32 -0
  67. package/dist/index.d.ts +46 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +369 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/patterns/index.d.ts +78 -0
  72. package/dist/patterns/index.d.ts.map +1 -0
  73. package/dist/patterns/index.js +264 -0
  74. package/dist/patterns/index.js.map +1 -0
  75. package/dist/prompts/index.d.ts +35 -0
  76. package/dist/prompts/index.d.ts.map +1 -0
  77. package/dist/prompts/index.js +270 -0
  78. package/dist/prompts/index.js.map +1 -0
  79. package/dist/skills/index.d.ts +3 -0
  80. package/dist/skills/index.d.ts.map +1 -0
  81. package/dist/skills/index.js +2 -0
  82. package/dist/skills/index.js.map +1 -0
  83. package/package.json +50 -0
@@ -0,0 +1,311 @@
1
+ # Error Handling in API Services
2
+
3
+ ## Description
4
+
5
+ Structured error handling in API services uses custom error classes with status codes, error details, and proper HTTP response formatting. This ensures consistent error responses across all API endpoints with full context for debugging and client-side error handling.
6
+
7
+ ## When to Use
8
+
9
+ **Use custom error handling** for:
10
+ - ✅ API endpoints that need consistent error responses
11
+ - ✅ Services that interact with external APIs or databases
12
+ - ✅ Situations where you need to distinguish error types and statuses
13
+ - ✅ Client-side error handling with structured error objects
14
+
15
+ ## Code Example
16
+
17
+ ### Custom Error Class Definition
18
+
19
+ ```typescript
20
+ // File: src/services/quantum-key-management.ts
21
+ export class QuantumApiError extends Error {
22
+ status: number;
23
+ details?: unknown;
24
+
25
+ constructor(message: string, status: number, details?: unknown) {
26
+ super(message);
27
+ this.name = 'QuantumApiError';
28
+ this.status = status;
29
+ this.details = details;
30
+
31
+ // Maintain proper prototype chain for instanceof checks
32
+ Object.setPrototypeOf(this, QuantumApiError.prototype);
33
+ }
34
+ }
35
+
36
+ // Usage in services
37
+ async function fetchQuantumProfile(
38
+ bearerClient: QuantumApiClient,
39
+ profileId: string
40
+ ): Promise<IbmProfileRecord> {
41
+ try {
42
+ const response = await bearerClient.getProfile(profileId);
43
+ return response;
44
+ } catch (err) {
45
+ if (err instanceof SdkQuantumApiError) {
46
+ throw new QuantumApiError(
47
+ `Failed to fetch profile: ${err.message}`,
48
+ err.statusCode || 500,
49
+ { originalError: err }
50
+ );
51
+ }
52
+ throw new QuantumApiError(
53
+ 'Unknown error fetching profile',
54
+ 500,
55
+ { originalError: err }
56
+ );
57
+ }
58
+ }
59
+ ```
60
+
61
+ **From:** DJsPortfolio/src/services/quantum-key-management.ts (lines 1-60)
62
+
63
+ ### SDK Client Error Mapping
64
+
65
+ ```typescript
66
+ // File: src/lib/quantum-sdk-executor.ts
67
+ import { QuantumApiError as SdkQuantumApiError } from '@mr.dj2u/quantum-api';
68
+
69
+ export type QuantumSdkEndpointExecutionResult = {
70
+ status: number;
71
+ statusText: string;
72
+ data: unknown;
73
+ };
74
+
75
+ async function executeQuantumEndpoint(
76
+ input: QuantumSdkEndpointExecutionInput
77
+ ): Promise<QuantumSdkEndpointExecutionResult> {
78
+ try {
79
+ // Select appropriate client based on auth method
80
+ const client = input.bearerToken
81
+ ? createQuantumBearerClient(input.baseUrl, input.bearerToken)
82
+ : createQuantumPublicClient(input.baseUrl);
83
+
84
+ // Execute request
85
+ const response = await client.request({
86
+ method: input.method,
87
+ path: input.path,
88
+ body: input.body,
89
+ });
90
+
91
+ return {
92
+ status: response.status,
93
+ statusText: response.statusText,
94
+ data: response.data,
95
+ };
96
+ } catch (err) {
97
+ if (err instanceof SdkQuantumApiError) {
98
+ return {
99
+ status: err.statusCode || 500,
100
+ statusText: 'Error',
101
+ data: {
102
+ error: err.message,
103
+ details: err.details,
104
+ },
105
+ };
106
+ }
107
+
108
+ return {
109
+ status: 500,
110
+ statusText: 'Internal Server Error',
111
+ data: {
112
+ error: 'Unexpected error executing endpoint',
113
+ },
114
+ };
115
+ }
116
+ }
117
+ ```
118
+
119
+ **From:** DJsPortfolio/src/lib/quantum-sdk-executor.ts (lines 1-50)
120
+
121
+ ### Error Response Format
122
+
123
+ ```typescript
124
+ // Standard error response structure
125
+ type ErrorResponse = {
126
+ error: string; // Human-readable error message
127
+ status: number; // HTTP status code
128
+ details?: unknown; // Additional error context
129
+ timestamp?: string; // When error occurred
130
+ };
131
+
132
+ // Return from API route
133
+ export async function POST(request: ExpoRequest) {
134
+ try {
135
+ const data = await request.json();
136
+ const result = await processQuantumRequest(data);
137
+ return new ExpoResponse(JSON.stringify(result), { status: 200 });
138
+ } catch (err) {
139
+ const errorResponse: ErrorResponse = {
140
+ error: err instanceof Error ? err.message : 'Unknown error',
141
+ status: err instanceof QuantumApiError ? err.status : 500,
142
+ details: err instanceof QuantumApiError ? err.details : undefined,
143
+ timestamp: new Date().toISOString(),
144
+ };
145
+
146
+ return new ExpoResponse(
147
+ JSON.stringify(errorResponse),
148
+ {
149
+ status: errorResponse.status,
150
+ headers: { 'Content-Type': 'application/json' },
151
+ }
152
+ );
153
+ }
154
+ }
155
+ ```
156
+
157
+ ## Configuration
158
+
159
+ ### Environment-Specific Error Handling
160
+
161
+ ```typescript
162
+ // config/error-handling.ts
163
+ export const errorConfig = {
164
+ development: {
165
+ exposeStackTraces: true,
166
+ logErrors: true,
167
+ verboseMessages: true,
168
+ },
169
+ production: {
170
+ exposeStackTraces: false,
171
+ logErrors: true,
172
+ verboseMessages: false,
173
+ },
174
+ };
175
+
176
+ export function shouldExposeErrorDetails(): boolean {
177
+ return errorConfig[process.env.NODE_ENV || 'development'].exposeStackTraces;
178
+ }
179
+ ```
180
+
181
+ ### Error Logging
182
+
183
+ ```typescript
184
+ // middleware/error-logger.ts
185
+ export function logError(err: Error, context?: Record<string, any>) {
186
+ const timestamp = new Date().toISOString();
187
+ const errorData = {
188
+ timestamp,
189
+ message: err.message,
190
+ name: err.name,
191
+ stack: err.stack,
192
+ context,
193
+ };
194
+
195
+ console.error(JSON.stringify(errorData));
196
+
197
+ // Could also send to external logging service
198
+ // sendToSentry(errorData);
199
+ }
200
+ ```
201
+
202
+ ## Best Practices
203
+
204
+ ### ✅ DO
205
+
206
+ 1. **Create typed error classes** for different error scenarios
207
+ ```typescript
208
+ class ValidationError extends Error {
209
+ constructor(public field: string, message: string) {
210
+ super(message);
211
+ this.name = 'ValidationError';
212
+ }
213
+ }
214
+
215
+ class AuthenticationError extends Error {
216
+ constructor(message: string) {
217
+ super(message);
218
+ this.name = 'AuthenticationError';
219
+ }
220
+ }
221
+ ```
222
+
223
+ 2. **Include status codes** with all errors
224
+ ```typescript
225
+ throw new ValidationError('email', 'Invalid email format');
226
+ // Map to 400 status in handler
227
+ ```
228
+
229
+ 3. **Log errors with context** for debugging
230
+ ```typescript
231
+ try {
232
+ await fetchData();
233
+ } catch (err) {
234
+ logError(err, {
235
+ userId: user.id,
236
+ action: 'fetch-profile',
237
+ timestamp: Date.now(),
238
+ });
239
+ }
240
+ ```
241
+
242
+ 4. **Distinguish error types** in client responses
243
+ ```typescript
244
+ const statusMap = {
245
+ ValidationError: 400,
246
+ AuthenticationError: 401,
247
+ NotFoundError: 404,
248
+ ServerError: 500,
249
+ };
250
+ ```
251
+
252
+ ### ❌ DON'T
253
+
254
+ 1. **Don't expose internal error details** in production
255
+ ```typescript
256
+ // ❌ BAD - leaks implementation details
257
+ return JSON.stringify({ error: err.stack });
258
+
259
+ // ✅ GOOD - generic message, log details server-side
260
+ return JSON.stringify({ error: 'Internal server error' });
261
+ ```
262
+
263
+ 2. **Don't ignore error types** from third-party libraries
264
+ ```typescript
265
+ // ❌ BAD - loses error context
266
+ catch (err) {
267
+ throw new Error('Failed');
268
+ }
269
+
270
+ // ✅ GOOD - map SDK errors to application errors
271
+ catch (err) {
272
+ if (err instanceof SdkError) {
273
+ throw new ApiError(err.message, err.statusCode);
274
+ }
275
+ }
276
+ ```
277
+
278
+ 3. **Don't return different error formats** across endpoints
279
+ ```typescript
280
+ // ❌ INCONSISTENT
281
+ Endpoint 1: { message: "Error" }
282
+ Endpoint 2: { error: "Error" }
283
+ Endpoint 3: { err: "Error" }
284
+
285
+ // ✅ CONSISTENT
286
+ All endpoints: { error, status, details, timestamp }
287
+ ```
288
+
289
+ 4. **Don't forget to set proper HTTP status codes**
290
+ ```typescript
291
+ // ❌ BAD - always returns 200
292
+ return new ExpoResponse(JSON.stringify(error), { status: 200 });
293
+
294
+ // ✅ GOOD - correct status codes
295
+ return new ExpoResponse(
296
+ JSON.stringify(error),
297
+ { status: error.status || 500 }
298
+ );
299
+ ```
300
+
301
+ ## Related Patterns
302
+
303
+ - [API Routes](./api-routes.md) — Route endpoint structure
304
+ - [CORS Configuration](./cors-configuration.md) — Origin validation
305
+ - [Health Endpoints](./health-endpoints.md) — Health check patterns
306
+
307
+ ---
308
+
309
+ *Pattern extracted from production repositories: DJsPortfolio, PokePages*
310
+ *Files: DJsPortfolio/src\services\quantum-key-management.ts*
311
+ *Lines 1-60 showing custom QuantumApiError class and error handling patterns*
@@ -0,0 +1,280 @@
1
+ # Drizzle ORM Schema Design
2
+
3
+ ## Description
4
+
5
+ Drizzle ORM schemas define type-safe database tables with full TypeScript inference from database types to application types. Schemas use domain-specific files with shared relations.ts for foreign keys and relationships, enabling compile-time type safety and automatic type generation.
6
+
7
+ ## When to Use
8
+
9
+ **Use Drizzle schemas** for:
10
+ - ✅ Any PostgreSQL table definition in React Native/Node.js applications
11
+ - ✅ Projects requiring full TypeScript type inference (no separate type definitions)
12
+ - ✅ Applications with complex relationships and joins
13
+ - ✅ Teams valuing compile-time safety over runtime convenience
14
+
15
+ ## Code Example
16
+
17
+ ### Basic Schema with Type Inference
18
+
19
+ ```typescript
20
+ // File: packages/db/src/schema/quantum-api/quantum-execution-jobs/schema.ts
21
+ import { pgTable, uuid, text, jsonb, integer, timestamp, unique, check } from 'drizzle-orm/pg-core';
22
+
23
+ export const quantumExecutionJobs = pgTable('quantum_execution_jobs', {
24
+ id: uuid('id').defaultRandom().primaryKey(),
25
+ provider: text('provider').notNull(), // 'ibm' | 'aws' | etc.
26
+ remoteJobId: text('remote_job_id').notNull(),
27
+ status: text('status').notNull().default('queued'), // queued | running | succeeded | failed | cancelled
28
+ requestPayload: jsonb('request_payload').notNull(), // Full request object
29
+ resultPayload: jsonb('result_payload'), // Result when status === succeeded
30
+ errorPayload: jsonb('error_payload'), // Error object when status === failed
31
+ createdAt: timestamp('created_at').defaultNow().notNull(),
32
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
33
+ completedAt: timestamp('completed_at'),
34
+ }, (table) => ({
35
+ // Composite unique constraint
36
+ uniqueRemoteJob: unique().on(table.provider, table.remoteJobId),
37
+
38
+ // Check constraints for valid status values
39
+ statusCheck: check(
40
+ 'status_check',
41
+ sql`status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')`
42
+ ),
43
+
44
+ // Ensure completedAt is set when status is terminal
45
+ completedAtCheck: check(
46
+ 'completed_at_check',
47
+ sql`(status IN ('succeeded', 'failed', 'cancelled') AND completed_at IS NOT NULL)
48
+ OR (status NOT IN ('succeeded', 'failed', 'cancelled') AND completed_at IS NULL)`
49
+ ),
50
+ }));
51
+
52
+ // Infer types from schema
53
+ export type QuantumExecutionJob = typeof quantumExecutionJobs.$inferSelect;
54
+ export type NewQuantumExecutionJob = typeof quantumExecutionJobs.$inferInsert;
55
+ ```
56
+
57
+ **From:** DJsPortfolio/packages/db/src/schema/quantum-api/quantum-execution-jobs/schema.ts (lines 1-69)
58
+
59
+ ### Schema with Foreign Keys
60
+
61
+ ```typescript
62
+ // File: src/db/eventClaimsSchema.ts (PokePages pattern)
63
+ import { pgTable, uuid, text, timestamp, integer, foreignKey } from 'drizzle-orm/pg-core';
64
+ import { profiles } from './profilesSchema';
65
+ import { events } from './eventsSchema';
66
+
67
+ export const eventClaims = pgTable(
68
+ 'event_claims',
69
+ {
70
+ id: uuid('id').defaultRandom().primaryKey(),
71
+ userId: uuid('user_id').notNull(),
72
+ eventId: uuid('event_id').notNull(),
73
+ claimedAt: timestamp('claimed_at').defaultNow().notNull(),
74
+ proofData: text('proof_data'), // Optional proof/screenshot
75
+ },
76
+ (table) => ({
77
+ userFk: foreignKey({
78
+ columns: [table.userId],
79
+ foreignColumns: [profiles.id],
80
+ name: 'event_claims_user_fk',
81
+ }).onDelete('cascade'),
82
+
83
+ eventFk: foreignKey({
84
+ columns: [table.eventId],
85
+ foreignColumns: [events.id],
86
+ name: 'event_claims_event_fk',
87
+ }).onDelete('cascade'),
88
+
89
+ // Unique constraint: user can only claim event once
90
+ uniqueUserEvent: unique().on(table.userId, table.eventId),
91
+ })
92
+ );
93
+
94
+ export type EventClaim = typeof eventClaims.$inferSelect;
95
+ export type NewEventClaim = typeof eventClaims.$inferInsert;
96
+ ```
97
+
98
+ **From:** PokePages/src/db/eventClaimsSchema.ts pattern
99
+
100
+ ### Indexed Schema for Performance
101
+
102
+ ```typescript
103
+ // File: src/db/postsSchema.ts
104
+ import { pgTable, uuid, text, timestamp, integer, index } from 'drizzle-orm/pg-core';
105
+
106
+ export const posts = pgTable(
107
+ 'posts',
108
+ {
109
+ id: uuid('id').defaultRandom().primaryKey(),
110
+ userId: uuid('user_id').notNull(),
111
+ title: text('title').notNull(),
112
+ content: text('content').notNull(),
113
+ likes: integer('likes').default(0),
114
+ createdAt: timestamp('created_at').defaultNow().notNull(),
115
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
116
+ deletedAt: timestamp('deleted_at'), // Soft delete
117
+ },
118
+ (table) => ({
119
+ // Single column indexes for common queries
120
+ userIdIdx: index('posts_user_id_idx').on(table.userId),
121
+ createdAtIdx: index('posts_created_at_idx').on(table.createdAt),
122
+
123
+ // Composite index for common query pattern
124
+ userCreatedIdx: index('posts_user_created_idx')
125
+ .on(table.userId, table.createdAt),
126
+
127
+ // Index for soft deletes
128
+ activePostsIdx: index('posts_active_idx')
129
+ .on(table.userId, table.deletedAt),
130
+ })
131
+ );
132
+ ```
133
+
134
+ ## Configuration
135
+
136
+ ### Database Connection Setup
137
+
138
+ ```typescript
139
+ // File: src/db/index.ts
140
+ import { drizzle } from 'drizzle-orm/postgres-js';
141
+ import postgres from 'postgres';
142
+ import * as schemas from './schemas';
143
+
144
+ const connectionString = process.env.DATABASE_URL!;
145
+
146
+ // Optimized connection pool for Supabase PgBouncer
147
+ export const client = postgres(connectionString, {
148
+ prepare: false, // Required for PgBouncer
149
+ ssl: 'require',
150
+ max: 3, // Small pool size
151
+ idle_timeout: 20, // Close idle connections
152
+ connect_timeout: 30, // Connection timeout
153
+ max_lifetime: 1800, // Recycle after 30 minutes
154
+ });
155
+
156
+ export const db = drizzle(client, {
157
+ schema: schemas,
158
+ });
159
+ ```
160
+
161
+ ### TypeScript Configuration
162
+
163
+ ```typescript
164
+ // File: tsconfig.json
165
+ {
166
+ "compilerOptions": {
167
+ "plugins": [
168
+ {
169
+ "name": "drizzle-orm/compiler",
170
+ "options": {
171
+ "casing": "snake_case"
172
+ }
173
+ }
174
+ ]
175
+ }
176
+ }
177
+ ```
178
+
179
+ ## Best Practices
180
+
181
+ ### ✅ DO
182
+
183
+ 1. **Infer types from schema** — don't maintain separate type definitions
184
+ ```typescript
185
+ // ✅ GOOD - single source of truth
186
+ export type Post = typeof posts.$inferSelect;
187
+ export type NewPost = typeof posts.$inferInsert;
188
+
189
+ // ❌ BAD - duplicate type definitions
190
+ interface Post {
191
+ id: string;
192
+ title: string;
193
+ // ... manually maintaining type sync
194
+ }
195
+ ```
196
+
197
+ 2. **Use domain-specific schema files**
198
+ ```
199
+ src/db/
200
+ ├── profilesSchema.ts
201
+ ├── postsSchema.ts
202
+ ├── commentsSchema.ts
203
+ ├── relations.ts ← all relationships defined here
204
+ └── index.ts
205
+ ```
206
+
207
+ 3. **Define relationships in shared relations.ts**
208
+ ```typescript
209
+ // File: src/db/relations.ts
210
+ import { relations } from 'drizzle-orm';
211
+ import { profiles, posts, comments } from './schemas';
212
+
213
+ export const profilesRelations = relations(profiles, ({ many }) => ({
214
+ posts: many(posts),
215
+ }));
216
+ ```
217
+
218
+ 4. **Use JSONB for flexible data** instead of many normalized tables
219
+ ```typescript
220
+ requestPayload: jsonb('request_payload').notNull(),
221
+ resultPayload: jsonb('result_payload'),
222
+ ```
223
+
224
+ 5. **Add check constraints** for data integrity
225
+ ```typescript
226
+ check(
227
+ 'status_check',
228
+ sql`status IN ('pending', 'completed', 'failed')`
229
+ )
230
+ ```
231
+
232
+ ### ❌ DON'T
233
+
234
+ 1. **Don't create overly normalized schemas** when embedding is more performant
235
+ ```typescript
236
+ // ❌ AVOID - creates N+1 queries
237
+ users table → user_profiles table → user_settings table
238
+
239
+ // ✅ BETTER - use JSONB for related data
240
+ users: { profile: jsonb, settings: jsonb }
241
+ ```
242
+
243
+ 2. **Don't forget indexes** on frequently queried columns
244
+ ```typescript
245
+ // ❌ BAD - no index on userId
246
+ posts table with userId but no index
247
+
248
+ // ✅ GOOD - indexed for fast lookups
249
+ userIdIdx: index().on(table.userId)
250
+ ```
251
+
252
+ 3. **Don't use mutable defaults** (like functions) for timestamps
253
+ ```typescript
254
+ // ❌ BAD - function called once at migration time
255
+ createdAt: timestamp('created_at').default(fn)
256
+
257
+ // ✅ GOOD - database handles default for each row
258
+ createdAt: timestamp('created_at').defaultNow()
259
+ ```
260
+
261
+ 4. **Don't create tables without soft delete** if you might need historical data
262
+ ```typescript
263
+ // ✅ GOOD - reversible deletion
264
+ deletedAt: timestamp('deleted_at')
265
+
266
+ // Query active records
267
+ where(isNull(posts.deletedAt))
268
+ ```
269
+
270
+ ## Related Patterns
271
+
272
+ - [Database Migrations](./migrations.md) — Managing schema changes
273
+ - [Relationships](./relations.md) — Foreign keys and joins
274
+ - [Query Organization](./query-organization.md) — Data access layer
275
+
276
+ ---
277
+
278
+ *Pattern extracted from production repositories: DJsPortfolio, PokePages, core-monorepo*
279
+ *Files: DJsPortfolio/packages\db\src\schema\quantum-api\quantum-execution-jobs\schema.ts*
280
+ *Lines 1-69 with full JSONB payload handling and check constraints*