@mr.dj2u/knowledge 0.1.6 → 0.1.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.
Files changed (74) hide show
  1. package/dist/content/checklists/push-merge-loop.md +17 -17
  2. package/dist/content/checklists/unified-agent-bundle-validation.md +9 -9
  3. package/dist/content/examples/push-merge-loop.md +14 -14
  4. package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -8
  5. package/dist/content/guides/animation-performance.md +31 -1
  6. package/dist/content/guides/post-create-onboarding.md +140 -140
  7. package/dist/content/patterns/animation/animation-motion-selection.md +155 -0
  8. package/dist/content/patterns/api/api-routes.md +313 -313
  9. package/dist/content/patterns/api/error-handling.md +310 -310
  10. package/dist/content/patterns/database/drizzle-schema.md +279 -279
  11. package/dist/content/patterns/database/migrations.md +364 -364
  12. package/dist/content/patterns/database/query-organization.md +536 -536
  13. package/dist/content/patterns/database/relations.md +449 -449
  14. package/dist/content/patterns/deployment/build-configuration.md +440 -440
  15. package/dist/content/patterns/deployment/ci-cd-patterns.md +447 -447
  16. package/dist/content/patterns/deployment/environment-config.md +379 -379
  17. package/dist/content/patterns/deployment/hosting-setup.md +424 -424
  18. package/dist/content/patterns/project/configuration-patterns.md +459 -459
  19. package/dist/content/patterns/project/documentation-org.md +506 -506
  20. package/dist/content/patterns/project/folder-structure.md +397 -397
  21. package/dist/content/patterns/project/library-exports.md +464 -464
  22. package/dist/content/patterns/project/monorepo-structure.md +500 -500
  23. package/dist/content/patterns/routing/dynamic-routes.md +220 -220
  24. package/dist/content/patterns/routing/file-based-routing.md +185 -185
  25. package/dist/content/patterns/routing/route-groups.md +428 -428
  26. package/dist/content/patterns/state/persistence-middleware.md +520 -520
  27. package/dist/content/patterns/state/selector-hooks.md +537 -537
  28. package/dist/content/patterns/state/store-organization.md +538 -538
  29. package/dist/content/patterns/state/zustand-patterns.md +347 -347
  30. package/dist/content/patterns/styling/component-styling.md +467 -467
  31. package/dist/content/patterns/styling/responsive-patterns.md +397 -397
  32. package/dist/content/patterns/styling/theme-configuration.md +425 -425
  33. package/dist/content/patterns/styling/uniwind-setup.md +411 -411
  34. package/dist/content/prompts/continue-development.md +35 -35
  35. package/dist/content/prompts/fix-seo.md +29 -29
  36. package/dist/content/prompts/onboard-new-expo-app.md +11 -11
  37. package/dist/content/prompts/prepare-deploy.md +29 -29
  38. package/dist/content/prompts/project-research-plan.md +29 -29
  39. package/dist/content/prompts/push-merge-loop.md +25 -25
  40. package/dist/content/prompts/review-expo-project.md +29 -29
  41. package/dist/content/prompts/review-motion.md +58 -0
  42. package/dist/content/prompts/run-doctor.md +38 -38
  43. package/dist/content/prompts/wrap-up.md +67 -67
  44. package/dist/content/reference/create-expo-stack-uniwind.md +29 -29
  45. package/dist/content/reference/mcp-sdk-transport.md +30 -30
  46. package/dist/content/reference/reference-repo-evacuation.md +31 -31
  47. package/dist/content/resource-index.json +3 -0
  48. package/dist/content/skills/animation-motion.md +68 -0
  49. package/dist/content/skills/api-routes.md +33 -33
  50. package/dist/content/skills/continue-development.md +32 -32
  51. package/dist/content/skills/debugging.md +32 -32
  52. package/dist/content/skills/deployment.md +32 -32
  53. package/dist/content/skills/dev-server-management.md +32 -32
  54. package/dist/content/skills/env-vars.md +32 -32
  55. package/dist/content/skills/expo-router-architecture.md +33 -33
  56. package/dist/content/skills/expo-ssr-safety.md +32 -32
  57. package/dist/content/skills/plugin-creation.md +41 -41
  58. package/dist/content/skills/production-server-patterns.md +31 -31
  59. package/dist/content/skills/project-onboarding.md +31 -31
  60. package/dist/content/skills/research-plan-intake.md +32 -32
  61. package/dist/content/skills/seo-metadata.md +31 -31
  62. package/dist/content/skills/super-stack-startup.md +34 -34
  63. package/dist/content/skills/uniwind-theming.md +32 -32
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +27 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/patterns/index.d.ts +9 -1
  68. package/dist/patterns/index.d.ts.map +1 -1
  69. package/dist/patterns/index.js +17 -0
  70. package/dist/patterns/index.js.map +1 -1
  71. package/dist/prompts/index.d.ts.map +1 -1
  72. package/dist/prompts/index.js +17 -0
  73. package/dist/prompts/index.js.map +1 -1
  74. package/package.json +6 -1
@@ -1,280 +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*
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
280
  *Lines 1-69 with full JSONB payload handling and check constraints*