@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.
- package/dist/content/checklists/ship-test-loop.md +16 -0
- package/dist/content/checklists/unified-agent-bundle-validation.md +8 -0
- package/dist/content/examples/ship-test-loop.md +13 -0
- package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -0
- package/dist/content/guides/animation-performance.md +30 -0
- package/dist/content/guides/post-create-onboarding.md +113 -0
- package/dist/content/patterns/api/api-routes.md +314 -0
- package/dist/content/patterns/api/error-handling.md +311 -0
- package/dist/content/patterns/database/drizzle-schema.md +280 -0
- package/dist/content/patterns/database/migrations.md +365 -0
- package/dist/content/patterns/database/query-organization.md +537 -0
- package/dist/content/patterns/database/relations.md +450 -0
- package/dist/content/patterns/deployment/build-configuration.md +452 -0
- package/dist/content/patterns/deployment/ci-cd-patterns.md +448 -0
- package/dist/content/patterns/deployment/environment-config.md +380 -0
- package/dist/content/patterns/deployment/hosting-setup.md +425 -0
- package/dist/content/patterns/project/configuration-patterns.md +459 -0
- package/dist/content/patterns/project/documentation-org.md +506 -0
- package/dist/content/patterns/project/folder-structure.md +398 -0
- package/dist/content/patterns/project/library-exports.md +465 -0
- package/dist/content/patterns/project/monorepo-structure.md +500 -0
- package/dist/content/patterns/routing/dynamic-routes.md +221 -0
- package/dist/content/patterns/routing/file-based-routing.md +186 -0
- package/dist/content/patterns/routing/route-groups.md +429 -0
- package/dist/content/patterns/state/persistence-middleware.md +521 -0
- package/dist/content/patterns/state/selector-hooks.md +538 -0
- package/dist/content/patterns/state/store-organization.md +539 -0
- package/dist/content/patterns/state/zustand-patterns.md +348 -0
- package/dist/content/patterns/styling/component-styling.md +468 -0
- package/dist/content/patterns/styling/responsive-patterns.md +398 -0
- package/dist/content/patterns/styling/theme-configuration.md +426 -0
- package/dist/content/patterns/styling/uniwind-setup.md +412 -0
- package/dist/content/prompts/continue-development.md +27 -0
- package/dist/content/prompts/create-expo-super-stack.md +29 -0
- package/dist/content/prompts/fix-seo.md +29 -0
- package/dist/content/prompts/onboard-new-expo-app.md +11 -0
- package/dist/content/prompts/prepare-deploy.md +29 -0
- package/dist/content/prompts/project-research-plan.md +29 -0
- package/dist/content/prompts/review-expo-project.md +29 -0
- package/dist/content/prompts/run-doctor.md +30 -0
- package/dist/content/prompts/ship-test-loop.md +24 -0
- package/dist/content/reference/create-expo-stack-uniwind.md +29 -0
- package/dist/content/reference/doctor-dogfood.md +42 -0
- package/dist/content/reference/mcp-sdk-transport.md +30 -0
- package/dist/content/reference/package-ci-patterns.md +24 -0
- package/dist/content/reference/reference-repo-evacuation.md +31 -0
- package/dist/content/resource-index.json +67 -0
- package/dist/content/rules/app-folder-architecture.md +13 -0
- package/dist/content/rules/env-hygiene.md +9 -0
- package/dist/content/rules/seo-metadata.md +7 -0
- package/dist/content/rules/ssr-safety.md +9 -0
- package/dist/content/skills/api-routes.md +33 -0
- package/dist/content/skills/continue-development.md +31 -0
- package/dist/content/skills/debugging.md +31 -0
- package/dist/content/skills/deployment.md +32 -0
- package/dist/content/skills/dev-server-management.md +31 -0
- package/dist/content/skills/env-vars.md +32 -0
- package/dist/content/skills/expo-router-architecture.md +33 -0
- package/dist/content/skills/expo-ssr-safety.md +32 -0
- package/dist/content/skills/plugin-creation.md +41 -0
- package/dist/content/skills/production-server-patterns.md +31 -0
- package/dist/content/skills/project-onboarding.md +31 -0
- package/dist/content/skills/research-plan-intake.md +31 -0
- package/dist/content/skills/seo-metadata.md +31 -0
- package/dist/content/skills/super-stack-startup.md +31 -0
- package/dist/content/skills/uniwind-theming.md +32 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +369 -0
- package/dist/index.js.map +1 -0
- package/dist/patterns/index.d.ts +78 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +264 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/prompts/index.d.ts +35 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +270 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/skills/index.d.ts +3 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# Table Relations Pattern
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Relations define how tables connect through foreign keys and reference relationships in Drizzle ORM. The `relations.ts` file explicitly declares one-to-many, many-to-one, and one-to-one relationships, enabling type-safe queries that fetch related data with full TypeScript inference.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
**Define relations when:**
|
|
10
|
+
- ✅ Table A references Table B (foreign key)
|
|
11
|
+
- ✅ Need to query parent + child data together
|
|
12
|
+
- ✅ Want type-safe relationship loading
|
|
13
|
+
- ✅ Implementing nested/relational queries
|
|
14
|
+
- ✅ Ensuring referential integrity
|
|
15
|
+
- ✅ Building flexible association patterns
|
|
16
|
+
|
|
17
|
+
## Core Concepts
|
|
18
|
+
|
|
19
|
+
**Relationship Types:**
|
|
20
|
+
1. **One-to-Many**: One profile has many posts
|
|
21
|
+
2. **Many-to-One**: Many posts belong to one profile
|
|
22
|
+
3. **One-to-One**: One profile has one settings
|
|
23
|
+
4. **Many-to-Many**: Many users have many groups (junction table)
|
|
24
|
+
|
|
25
|
+
**Relation Declaration:**
|
|
26
|
+
```typescript
|
|
27
|
+
// Foreign key in database:
|
|
28
|
+
posts.user_id → profiles.id
|
|
29
|
+
|
|
30
|
+
// In relations.ts:
|
|
31
|
+
export const profilesRelations = relations(profiles, ({ many }) => ({
|
|
32
|
+
posts: many(posts), // One profile can have many posts
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
export const postsRelations = relations(posts, ({ one }) => ({
|
|
36
|
+
author: one(profiles, {
|
|
37
|
+
fields: [posts.userId],
|
|
38
|
+
references: [profiles.id],
|
|
39
|
+
}),
|
|
40
|
+
}));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Type Safety:**
|
|
44
|
+
- Zustand stores for state: `User | null`
|
|
45
|
+
- Drizzle types from schema: `typeof profiles.$inferSelect`
|
|
46
|
+
- Relations enable: `profile.posts` with full type checking
|
|
47
|
+
|
|
48
|
+
## Code Examples
|
|
49
|
+
|
|
50
|
+
### Basic One-to-Many Relations
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// File: src/db/profilesSchema.ts
|
|
54
|
+
import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core';
|
|
55
|
+
|
|
56
|
+
export const profiles = pgTable('profiles', {
|
|
57
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
58
|
+
username: text('username').notNull().unique(),
|
|
59
|
+
email: text('email').notNull().unique(),
|
|
60
|
+
bio: text('bio'),
|
|
61
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export type Profile = typeof profiles.$inferSelect;
|
|
65
|
+
|
|
66
|
+
// File: src/db/postsSchema.ts
|
|
67
|
+
export const posts = pgTable('posts', {
|
|
68
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
69
|
+
userId: uuid('user_id')
|
|
70
|
+
.notNull()
|
|
71
|
+
.references(() => profiles.id, { onDelete: 'cascade' }),
|
|
72
|
+
title: text('title').notNull(),
|
|
73
|
+
content: text('content').notNull(),
|
|
74
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export type Post = typeof posts.$inferSelect;
|
|
78
|
+
|
|
79
|
+
// File: src/db/relations.ts
|
|
80
|
+
import { relations } from 'drizzle-orm';
|
|
81
|
+
import { profiles, posts } from './schemas';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* One profile has many posts
|
|
85
|
+
*/
|
|
86
|
+
export const profilesRelations = relations(profiles, ({ many }) => ({
|
|
87
|
+
posts: many(posts),
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Many posts belong to one profile
|
|
92
|
+
*/
|
|
93
|
+
export const postsRelations = relations(posts, ({ one }) => ({
|
|
94
|
+
author: one(profiles, {
|
|
95
|
+
fields: [posts.userId],
|
|
96
|
+
references: [profiles.id],
|
|
97
|
+
}),
|
|
98
|
+
}));
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Query with Relations
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// File: src/db/postsQueries.ts
|
|
105
|
+
import { db } from './index';
|
|
106
|
+
import { posts, profiles } from './schemas';
|
|
107
|
+
import { eq } from 'drizzle-orm';
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get post with author data
|
|
111
|
+
*/
|
|
112
|
+
export async function getPostWithAuthor(postId: string) {
|
|
113
|
+
return await db.query.posts.findFirst({
|
|
114
|
+
where: eq(posts.id, postId),
|
|
115
|
+
with: {
|
|
116
|
+
author: true, // Load related profile
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Result type:
|
|
122
|
+
// {
|
|
123
|
+
// id: string;
|
|
124
|
+
// title: string;
|
|
125
|
+
// content: string;
|
|
126
|
+
// userId: string;
|
|
127
|
+
// createdAt: Date;
|
|
128
|
+
// author: {
|
|
129
|
+
// id: string;
|
|
130
|
+
// username: string;
|
|
131
|
+
// email: string;
|
|
132
|
+
// bio: string | null;
|
|
133
|
+
// createdAt: Date;
|
|
134
|
+
// }
|
|
135
|
+
// }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Complex Relations: Many-to-Many
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// File: src/db/usersSchema.ts
|
|
142
|
+
export const users = pgTable('users', {
|
|
143
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
144
|
+
username: text('username').notNull().unique(),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// File: src/db/groupsSchema.ts
|
|
148
|
+
export const groups = pgTable('groups', {
|
|
149
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
150
|
+
name: text('name').notNull(),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// File: src/db/userGroupsSchema.ts (Junction table)
|
|
154
|
+
export const userGroups = pgTable('user_groups', {
|
|
155
|
+
userId: uuid('user_id')
|
|
156
|
+
.notNull()
|
|
157
|
+
.references(() => users.id, { onDelete: 'cascade' }),
|
|
158
|
+
groupId: uuid('group_id')
|
|
159
|
+
.notNull()
|
|
160
|
+
.references(() => groups.id, { onDelete: 'cascade' }),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// File: src/db/relations.ts
|
|
164
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
165
|
+
userGroups: many(userGroups), // Users have many group memberships
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
export const groupsRelations = relations(groups, ({ many }) => ({
|
|
169
|
+
userGroups: many(userGroups), // Groups have many user members
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
export const userGroupsRelations = relations(userGroups, ({ one }) => ({
|
|
173
|
+
user: one(users, {
|
|
174
|
+
fields: [userGroups.userId],
|
|
175
|
+
references: [users.id],
|
|
176
|
+
}),
|
|
177
|
+
group: one(groups, {
|
|
178
|
+
fields: [userGroups.groupId],
|
|
179
|
+
references: [groups.id],
|
|
180
|
+
}),
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
// Query usage:
|
|
184
|
+
const userWithGroups = await db.query.users.findFirst({
|
|
185
|
+
where: eq(users.id, userId),
|
|
186
|
+
with: {
|
|
187
|
+
userGroups: {
|
|
188
|
+
with: {
|
|
189
|
+
group: true, // Nested relations
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### One-to-One Relations
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// File: src/db/userSettingsSchema.ts
|
|
200
|
+
export const userSettings = pgTable('user_settings', {
|
|
201
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
202
|
+
userId: uuid('user_id')
|
|
203
|
+
.notNull()
|
|
204
|
+
.unique() // One-to-one: unique constraint
|
|
205
|
+
.references(() => users.id, { onDelete: 'cascade' }),
|
|
206
|
+
theme: text('theme').default('auto'),
|
|
207
|
+
notificationsEnabled: boolean('notifications_enabled').default(true),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// File: src/db/relations.ts
|
|
211
|
+
export const usersRelations = relations(users, ({ one }) => ({
|
|
212
|
+
settings: one(userSettings, {
|
|
213
|
+
fields: [users.id],
|
|
214
|
+
references: [userSettings.userId],
|
|
215
|
+
}),
|
|
216
|
+
}));
|
|
217
|
+
|
|
218
|
+
export const userSettingsRelations = relations(userSettings, ({ one }) => ({
|
|
219
|
+
user: one(users, {
|
|
220
|
+
fields: [userSettings.userId],
|
|
221
|
+
references: [users.id],
|
|
222
|
+
}),
|
|
223
|
+
}));
|
|
224
|
+
|
|
225
|
+
// Query:
|
|
226
|
+
const userWithSettings = await db.query.users.findFirst({
|
|
227
|
+
where: eq(users.id, userId),
|
|
228
|
+
with: {
|
|
229
|
+
settings: true,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Nested Relations Query
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// File: src/db/queriesAdvanced.ts
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get user with posts, comments, and comment authors
|
|
241
|
+
* Deeply nested relations
|
|
242
|
+
*/
|
|
243
|
+
export async function getUserWithContent(userId: string) {
|
|
244
|
+
return await db.query.users.findFirst({
|
|
245
|
+
where: eq(users.id, userId),
|
|
246
|
+
with: {
|
|
247
|
+
// User's posts
|
|
248
|
+
posts: {
|
|
249
|
+
with: {
|
|
250
|
+
// Posts' comments
|
|
251
|
+
comments: {
|
|
252
|
+
with: {
|
|
253
|
+
// Comments' authors
|
|
254
|
+
author: true,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Result structure:
|
|
264
|
+
// {
|
|
265
|
+
// id: string;
|
|
266
|
+
// username: string;
|
|
267
|
+
// posts: [
|
|
268
|
+
// {
|
|
269
|
+
// id: string;
|
|
270
|
+
// title: string;
|
|
271
|
+
// comments: [
|
|
272
|
+
// {
|
|
273
|
+
// id: string;
|
|
274
|
+
// text: string;
|
|
275
|
+
// author: {
|
|
276
|
+
// id: string;
|
|
277
|
+
// username: string;
|
|
278
|
+
// }
|
|
279
|
+
// }
|
|
280
|
+
// ]
|
|
281
|
+
// }
|
|
282
|
+
// ]
|
|
283
|
+
// }
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Filtering with Relations
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// File: src/db/queriesWithFilters.ts
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get profiles that have published posts
|
|
293
|
+
*/
|
|
294
|
+
export async function getProfilesWithPosts() {
|
|
295
|
+
return await db.query.profiles.findMany({
|
|
296
|
+
with: {
|
|
297
|
+
posts: {
|
|
298
|
+
where: eq(posts.published, true),
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get recent posts with author (active authors only)
|
|
306
|
+
*/
|
|
307
|
+
export async function getRecentPosts() {
|
|
308
|
+
return await db.query.posts.findMany({
|
|
309
|
+
orderBy: (posts) => desc(posts.createdAt),
|
|
310
|
+
limit: 10,
|
|
311
|
+
with: {
|
|
312
|
+
author: {
|
|
313
|
+
columns: {
|
|
314
|
+
username: true,
|
|
315
|
+
profileImage: true,
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Relation Best Practices
|
|
324
|
+
|
|
325
|
+
### ✅ DO
|
|
326
|
+
|
|
327
|
+
1. **Define relations in dedicated file**
|
|
328
|
+
```typescript
|
|
329
|
+
// ✅ RIGHT - src/db/relations.ts
|
|
330
|
+
export const profilesRelations = relations(...);
|
|
331
|
+
export const postsRelations = relations(...);
|
|
332
|
+
|
|
333
|
+
// ✅ Import in index.ts
|
|
334
|
+
import { profilesRelations, postsRelations } from './relations';
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
2. **Export from index.ts**
|
|
338
|
+
```typescript
|
|
339
|
+
// File: src/db/index.ts
|
|
340
|
+
export { db } from './client';
|
|
341
|
+
export { profiles, posts } from './schemas';
|
|
342
|
+
export * from './relations'; // Make relations available
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
3. **Use type-safe queries**
|
|
346
|
+
```typescript
|
|
347
|
+
// ✅ RIGHT - Full type inference
|
|
348
|
+
const postWithAuthor = await db.query.posts.findFirst({
|
|
349
|
+
where: eq(posts.id, postId),
|
|
350
|
+
with: {
|
|
351
|
+
author: true, // TypeScript knows author: Profile
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
4. **Name relations descriptively**
|
|
357
|
+
```typescript
|
|
358
|
+
// ✅ RIGHT - Clear relationship names
|
|
359
|
+
profiles: many(posts); // Posts written by profile
|
|
360
|
+
author: one(profiles); // Profile who wrote this post
|
|
361
|
+
|
|
362
|
+
// ❌ WRONG - Unclear
|
|
363
|
+
items: many(posts);
|
|
364
|
+
owner: one(profiles);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### ❌ DON'T
|
|
368
|
+
|
|
369
|
+
1. **Don't define relations in schema files**
|
|
370
|
+
```typescript
|
|
371
|
+
// ❌ WRONG - Mixing concerns
|
|
372
|
+
// src/db/profilesSchema.ts
|
|
373
|
+
export const profilesRelations = relations(...);
|
|
374
|
+
|
|
375
|
+
// ✅ RIGHT - Dedicated relations.ts
|
|
376
|
+
// src/db/relations.ts
|
|
377
|
+
export const profilesRelations = relations(...);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
2. **Don't forget to import relations in db/index.ts**
|
|
381
|
+
```typescript
|
|
382
|
+
// ❌ WRONG - Relations not exported
|
|
383
|
+
// src/db/index.ts
|
|
384
|
+
export { db } from './client';
|
|
385
|
+
// (relations.ts exists but not imported)
|
|
386
|
+
|
|
387
|
+
// ✅ RIGHT
|
|
388
|
+
import * from './relations'; // Make relations available
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
3. **Don't load unnecessary relations**
|
|
392
|
+
```typescript
|
|
393
|
+
// ❌ WRONG - Loading relation you don't use
|
|
394
|
+
const post = await db.query.posts.findFirst({
|
|
395
|
+
where: eq(posts.id, postId),
|
|
396
|
+
with: {
|
|
397
|
+
author: true,
|
|
398
|
+
comments: true,
|
|
399
|
+
tags: true,
|
|
400
|
+
relatedPosts: true,
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
// Only using post.author
|
|
404
|
+
|
|
405
|
+
// ✅ RIGHT - Load only needed relations
|
|
406
|
+
const post = await db.query.posts.findFirst({
|
|
407
|
+
where: eq(posts.id, postId),
|
|
408
|
+
with: {
|
|
409
|
+
author: true,
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Referential Integrity
|
|
415
|
+
|
|
416
|
+
### Foreign Key Constraints
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// File: src/db/postsSchema.ts
|
|
420
|
+
|
|
421
|
+
export const posts = pgTable('posts', {
|
|
422
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
423
|
+
userId: uuid('user_id')
|
|
424
|
+
.notNull()
|
|
425
|
+
.references(() => profiles.id, {
|
|
426
|
+
onDelete: 'cascade', // Delete posts when user deleted
|
|
427
|
+
onUpdate: 'cascade', // Update userId when user.id changes
|
|
428
|
+
}),
|
|
429
|
+
title: text('title').notNull(),
|
|
430
|
+
content: text('content').notNull(),
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### ON DELETE Options
|
|
435
|
+
|
|
436
|
+
- `cascade`: Delete child records when parent deleted
|
|
437
|
+
- `restrict`: Prevent parent deletion if children exist
|
|
438
|
+
- `set null`: Set foreign key to NULL when parent deleted
|
|
439
|
+
- `no action`: Raise error (requires manual cleanup)
|
|
440
|
+
|
|
441
|
+
## Related Patterns
|
|
442
|
+
|
|
443
|
+
- [Drizzle Schema](./drizzle-schema.md) — Schema definitions
|
|
444
|
+
- [Migrations](./migrations.md) — Schema versioning
|
|
445
|
+
- [Query Organization](./query-organization.md) — Query patterns
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
*Pattern extracted from production repositories: core-monorepo, PokePages, quantum-api*
|
|
450
|
+
*Files: src/db/relations.ts, query builders with nested relations*
|