omgkit 2.1.1 → 2.2.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 (50) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  5. package/plugin/skills/databases/redis/SKILL.md +885 -25
  6. package/plugin/skills/devops/aws/SKILL.md +686 -28
  7. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  8. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  9. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  10. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  12. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  16. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  17. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  18. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  19. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  20. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  21. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  22. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  23. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  24. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  25. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  26. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  27. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  28. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  29. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  30. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  31. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  32. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  33. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  34. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  35. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  36. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  37. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  38. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  39. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  40. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  41. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  42. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  43. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  44. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  45. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  46. package/plugin/skills/security/oauth/SKILL.md +968 -31
  47. package/plugin/skills/security/owasp/SKILL.md +894 -33
  48. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  49. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  50. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,55 +1,801 @@
1
1
  ---
2
2
  name: prisma
3
- description: Prisma ORM. Use for database access, migrations, type-safe queries.
3
+ description: Prisma ORM with type-safe queries, migrations, relations, and production database patterns
4
+ category: databases
5
+ triggers:
6
+ - prisma
7
+ - prisma client
8
+ - prisma migrate
9
+ - prisma schema
10
+ - orm
11
+ - type-safe database
4
12
  ---
5
13
 
6
- # Prisma Skill
14
+ # Prisma
15
+
16
+ Enterprise-grade **Prisma ORM** development following industry best practices. This skill covers schema design, type-safe queries, migrations, relations, transactions, and production-ready patterns used by top engineering teams.
17
+
18
+ ## Purpose
19
+
20
+ Build type-safe database applications with confidence:
21
+
22
+ - Design comprehensive Prisma schemas
23
+ - Write type-safe database queries
24
+ - Manage database migrations
25
+ - Handle complex relations
26
+ - Implement transactions
27
+ - Optimize query performance
28
+ - Deploy to production
29
+
30
+ ## Features
31
+
32
+ ### 1. Schema Design
7
33
 
8
- ## Schema
9
34
  ```prisma
35
+ // prisma/schema.prisma
36
+ generator client {
37
+ provider = "prisma-client-js"
38
+ previewFeatures = ["fullTextSearch", "fullTextIndex"]
39
+ }
40
+
41
+ datasource db {
42
+ provider = "postgresql"
43
+ url = env("DATABASE_URL")
44
+ }
45
+
46
+ // Enums
47
+ enum UserRole {
48
+ ADMIN
49
+ USER
50
+ GUEST
51
+ }
52
+
53
+ enum OrderStatus {
54
+ PENDING
55
+ CONFIRMED
56
+ SHIPPED
57
+ DELIVERED
58
+ CANCELLED
59
+ }
60
+
61
+ // User model
10
62
  model User {
11
63
  id String @id @default(cuid())
12
64
  email String @unique
13
- posts Post[]
65
+ password String
66
+ role UserRole @default(USER)
67
+ isActive Boolean @default(true)
14
68
  createdAt DateTime @default(now())
69
+ updatedAt DateTime @updatedAt
70
+
71
+ // Relations
72
+ profile Profile?
73
+ posts Post[]
74
+ orders Order[]
75
+ memberships Membership[]
76
+ ownedOrgs Organization[] @relation("OrganizationOwner")
77
+
78
+ @@index([email])
79
+ @@index([role, isActive])
80
+ @@map("users")
81
+ }
82
+
83
+ // One-to-one relation
84
+ model Profile {
85
+ id String @id @default(cuid())
86
+ firstName String
87
+ lastName String
88
+ avatar String?
89
+ bio String?
90
+
91
+ userId String @unique
92
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
93
+
94
+ @@map("profiles")
15
95
  }
16
96
 
97
+ // One-to-many relation
17
98
  model Post {
18
- id String @id @default(cuid())
19
- title String
20
- author User @relation(fields: [authorId], references: [id])
99
+ id String @id @default(cuid())
100
+ title String
101
+ slug String @unique
102
+ content String
103
+ published Boolean @default(false)
104
+ publishedAt DateTime?
105
+ createdAt DateTime @default(now())
106
+ updatedAt DateTime @updatedAt
107
+
21
108
  authorId String
109
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
110
+
111
+ categories Category[]
112
+ tags Tag[]
113
+
114
+ @@index([authorId])
115
+ @@index([published, publishedAt])
116
+ @@fulltext([title, content])
117
+ @@map("posts")
118
+ }
119
+
120
+ // Many-to-many relation (explicit)
121
+ model Category {
122
+ id String @id @default(cuid())
123
+ name String @unique
124
+ slug String @unique
125
+ posts Post[]
126
+
127
+ @@map("categories")
128
+ }
129
+
130
+ model Tag {
131
+ id String @id @default(cuid())
132
+ name String @unique
133
+ posts Post[]
134
+
135
+ @@map("tags")
136
+ }
137
+
138
+ // Order with items (one-to-many)
139
+ model Order {
140
+ id String @id @default(cuid())
141
+ status OrderStatus @default(PENDING)
142
+ total Decimal @db.Decimal(10, 2)
143
+ createdAt DateTime @default(now())
144
+ updatedAt DateTime @updatedAt
145
+
146
+ userId String
147
+ user User @relation(fields: [userId], references: [id])
148
+
149
+ items OrderItem[]
150
+
151
+ @@index([userId, status])
152
+ @@index([createdAt])
153
+ @@map("orders")
154
+ }
155
+
156
+ model OrderItem {
157
+ id String @id @default(cuid())
158
+ quantity Int
159
+ price Decimal @db.Decimal(10, 2)
160
+
161
+ orderId String
162
+ order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
163
+
164
+ productId String
165
+ product Product @relation(fields: [productId], references: [id])
166
+
167
+ @@map("order_items")
168
+ }
169
+
170
+ model Product {
171
+ id String @id @default(cuid())
172
+ name String
173
+ description String?
174
+ price Decimal @db.Decimal(10, 2)
175
+ stock Int @default(0)
176
+ createdAt DateTime @default(now())
177
+
178
+ orderItems OrderItem[]
179
+
180
+ @@map("products")
181
+ }
182
+
183
+ // Many-to-many with extra fields
184
+ model Organization {
185
+ id String @id @default(cuid())
186
+ name String
187
+ slug String @unique
188
+ createdAt DateTime @default(now())
189
+
190
+ ownerId String
191
+ owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
192
+
193
+ memberships Membership[]
194
+
195
+ @@map("organizations")
196
+ }
197
+
198
+ model Membership {
199
+ id String @id @default(cuid())
200
+ role String @default("member")
201
+ joinedAt DateTime @default(now())
202
+
203
+ userId String
204
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
205
+
206
+ organizationId String
207
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
208
+
209
+ @@unique([userId, organizationId])
210
+ @@map("memberships")
22
211
  }
23
212
  ```
24
213
 
25
- ## Queries
214
+ ### 2. Type-Safe Queries
215
+
26
216
  ```typescript
27
- // Create
28
- const user = await prisma.user.create({
29
- data: { email: 'test@example.com' }
30
- });
217
+ // src/repositories/user.repository.ts
218
+ import { PrismaClient, Prisma, User, UserRole } from '@prisma/client';
31
219
 
32
- // Find with relations
33
- const users = await prisma.user.findMany({
34
- include: { posts: true },
35
- where: { email: { contains: '@example.com' } }
36
- });
220
+ const prisma = new PrismaClient();
221
+
222
+ // Include types for relations
223
+ type UserWithProfile = Prisma.UserGetPayload<{
224
+ include: { profile: true };
225
+ }>;
226
+
227
+ type UserWithOrganizations = Prisma.UserGetPayload<{
228
+ include: {
229
+ memberships: {
230
+ include: { organization: true };
231
+ };
232
+ };
233
+ }>;
234
+
235
+ export class UserRepository {
236
+ // Create with profile
237
+ async createWithProfile(data: {
238
+ email: string;
239
+ password: string;
240
+ profile: { firstName: string; lastName: string };
241
+ }): Promise<UserWithProfile> {
242
+ return prisma.user.create({
243
+ data: {
244
+ email: data.email,
245
+ password: data.password,
246
+ profile: {
247
+ create: {
248
+ firstName: data.profile.firstName,
249
+ lastName: data.profile.lastName,
250
+ },
251
+ },
252
+ },
253
+ include: { profile: true },
254
+ });
255
+ }
256
+
257
+ // Find with filters
258
+ async findMany(params: {
259
+ skip?: number;
260
+ take?: number;
261
+ where?: Prisma.UserWhereInput;
262
+ orderBy?: Prisma.UserOrderByWithRelationInput;
263
+ }) {
264
+ const { skip, take, where, orderBy } = params;
265
+
266
+ const [users, total] = await prisma.$transaction([
267
+ prisma.user.findMany({
268
+ skip,
269
+ take,
270
+ where,
271
+ orderBy,
272
+ include: { profile: true },
273
+ }),
274
+ prisma.user.count({ where }),
275
+ ]);
276
+
277
+ return {
278
+ data: users,
279
+ pagination: {
280
+ total,
281
+ page: skip ? Math.floor(skip / (take || 20)) + 1 : 1,
282
+ limit: take || 20,
283
+ totalPages: Math.ceil(total / (take || 20)),
284
+ },
285
+ };
286
+ }
287
+
288
+ // Search users
289
+ async search(query: string, role?: UserRole) {
290
+ return prisma.user.findMany({
291
+ where: {
292
+ AND: [
293
+ role ? { role } : {},
294
+ {
295
+ OR: [
296
+ { email: { contains: query, mode: 'insensitive' } },
297
+ { profile: { firstName: { contains: query, mode: 'insensitive' } } },
298
+ { profile: { lastName: { contains: query, mode: 'insensitive' } } },
299
+ ],
300
+ },
301
+ ],
302
+ },
303
+ include: { profile: true },
304
+ });
305
+ }
306
+
307
+ // Update with upsert for profile
308
+ async updateProfile(
309
+ userId: string,
310
+ data: { firstName?: string; lastName?: string; bio?: string }
311
+ ) {
312
+ return prisma.user.update({
313
+ where: { id: userId },
314
+ data: {
315
+ profile: {
316
+ upsert: {
317
+ create: {
318
+ firstName: data.firstName || '',
319
+ lastName: data.lastName || '',
320
+ bio: data.bio,
321
+ },
322
+ update: data,
323
+ },
324
+ },
325
+ },
326
+ include: { profile: true },
327
+ });
328
+ }
329
+
330
+ // Delete with cascade
331
+ async delete(userId: string) {
332
+ return prisma.user.delete({
333
+ where: { id: userId },
334
+ });
335
+ }
336
+
337
+ // Get user with organizations
338
+ async getUserWithOrganizations(userId: string): Promise<UserWithOrganizations | null> {
339
+ return prisma.user.findUnique({
340
+ where: { id: userId },
341
+ include: {
342
+ memberships: {
343
+ include: {
344
+ organization: true,
345
+ },
346
+ },
347
+ },
348
+ });
349
+ }
350
+ }
351
+ ```
352
+
353
+ ### 3. Transactions
354
+
355
+ ```typescript
356
+ // src/services/order.service.ts
357
+ import { PrismaClient, Prisma } from '@prisma/client';
358
+
359
+ const prisma = new PrismaClient();
360
+
361
+ interface OrderItem {
362
+ productId: string;
363
+ quantity: number;
364
+ }
365
+
366
+ export class OrderService {
367
+ // Interactive transaction for creating order
368
+ async createOrder(userId: string, items: OrderItem[]) {
369
+ return prisma.$transaction(async (tx) => {
370
+ // Validate and get products
371
+ const productIds = items.map((i) => i.productId);
372
+ const products = await tx.product.findMany({
373
+ where: { id: { in: productIds } },
374
+ });
375
+
376
+ if (products.length !== items.length) {
377
+ throw new Error('Some products not found');
378
+ }
379
+
380
+ // Check stock and calculate total
381
+ let total = new Prisma.Decimal(0);
382
+ const orderItems: Prisma.OrderItemCreateManyOrderInput[] = [];
37
383
 
38
- // Update
39
- await prisma.user.update({
40
- where: { id },
41
- data: { email: 'new@example.com' }
384
+ for (const item of items) {
385
+ const product = products.find((p) => p.id === item.productId)!;
386
+
387
+ if (product.stock < item.quantity) {
388
+ throw new Error(`Insufficient stock for ${product.name}`);
389
+ }
390
+
391
+ // Update stock
392
+ await tx.product.update({
393
+ where: { id: product.id },
394
+ data: { stock: { decrement: item.quantity } },
395
+ });
396
+
397
+ const itemTotal = product.price.mul(item.quantity);
398
+ total = total.add(itemTotal);
399
+
400
+ orderItems.push({
401
+ productId: product.id,
402
+ quantity: item.quantity,
403
+ price: product.price,
404
+ });
405
+ }
406
+
407
+ // Create order with items
408
+ return tx.order.create({
409
+ data: {
410
+ userId,
411
+ total,
412
+ items: {
413
+ createMany: { data: orderItems },
414
+ },
415
+ },
416
+ include: {
417
+ items: {
418
+ include: { product: true },
419
+ },
420
+ },
421
+ });
422
+ });
423
+ }
424
+
425
+ // Sequential transaction for batch operations
426
+ async batchUpdateOrderStatus(
427
+ orderIds: string[],
428
+ status: 'CONFIRMED' | 'SHIPPED' | 'DELIVERED'
429
+ ) {
430
+ const operations = orderIds.map((id) =>
431
+ prisma.order.update({
432
+ where: { id },
433
+ data: { status },
434
+ })
435
+ );
436
+
437
+ return prisma.$transaction(operations);
438
+ }
439
+
440
+ // Cancel order with stock restoration
441
+ async cancelOrder(orderId: string) {
442
+ return prisma.$transaction(async (tx) => {
443
+ const order = await tx.order.findUnique({
444
+ where: { id: orderId },
445
+ include: { items: true },
446
+ });
447
+
448
+ if (!order) {
449
+ throw new Error('Order not found');
450
+ }
451
+
452
+ if (order.status !== 'PENDING') {
453
+ throw new Error('Only pending orders can be cancelled');
454
+ }
455
+
456
+ // Restore stock
457
+ for (const item of order.items) {
458
+ await tx.product.update({
459
+ where: { id: item.productId },
460
+ data: { stock: { increment: item.quantity } },
461
+ });
462
+ }
463
+
464
+ // Update order status
465
+ return tx.order.update({
466
+ where: { id: orderId },
467
+ data: { status: 'CANCELLED' },
468
+ });
469
+ });
470
+ }
471
+ }
472
+ ```
473
+
474
+ ### 4. Migrations
475
+
476
+ ```bash
477
+ # Generate migration from schema changes
478
+ npx prisma migrate dev --name add_user_profile
479
+
480
+ # Apply migrations in production
481
+ npx prisma migrate deploy
482
+
483
+ # Reset database (development only)
484
+ npx prisma migrate reset
485
+
486
+ # Generate Prisma Client
487
+ npx prisma generate
488
+
489
+ # Open Prisma Studio
490
+ npx prisma studio
491
+ ```
492
+
493
+ ```typescript
494
+ // prisma/seed.ts
495
+ import { PrismaClient } from '@prisma/client';
496
+ import { hash } from 'bcrypt';
497
+
498
+ const prisma = new PrismaClient();
499
+
500
+ async function main() {
501
+ // Create admin user
502
+ const adminPassword = await hash('admin123', 12);
503
+ const admin = await prisma.user.upsert({
504
+ where: { email: 'admin@example.com' },
505
+ update: {},
506
+ create: {
507
+ email: 'admin@example.com',
508
+ password: adminPassword,
509
+ role: 'ADMIN',
510
+ profile: {
511
+ create: {
512
+ firstName: 'Admin',
513
+ lastName: 'User',
514
+ },
515
+ },
516
+ },
517
+ });
518
+
519
+ // Create categories
520
+ const categories = await Promise.all(
521
+ ['Technology', 'Design', 'Business'].map((name) =>
522
+ prisma.category.upsert({
523
+ where: { slug: name.toLowerCase() },
524
+ update: {},
525
+ create: {
526
+ name,
527
+ slug: name.toLowerCase(),
528
+ },
529
+ })
530
+ )
531
+ );
532
+
533
+ // Create products
534
+ const products = await prisma.product.createMany({
535
+ data: [
536
+ { name: 'Product 1', price: 29.99, stock: 100 },
537
+ { name: 'Product 2', price: 49.99, stock: 50 },
538
+ { name: 'Product 3', price: 99.99, stock: 25 },
539
+ ],
540
+ skipDuplicates: true,
541
+ });
542
+
543
+ console.log({ admin, categories, products });
544
+ }
545
+
546
+ main()
547
+ .catch((e) => {
548
+ console.error(e);
549
+ process.exit(1);
550
+ })
551
+ .finally(async () => {
552
+ await prisma.$disconnect();
553
+ });
554
+ ```
555
+
556
+ ### 5. Advanced Queries
557
+
558
+ ```typescript
559
+ // src/repositories/post.repository.ts
560
+ import { PrismaClient, Prisma } from '@prisma/client';
561
+
562
+ const prisma = new PrismaClient();
563
+
564
+ export class PostRepository {
565
+ // Full-text search
566
+ async searchPosts(query: string) {
567
+ return prisma.post.findMany({
568
+ where: {
569
+ OR: [
570
+ { title: { search: query } },
571
+ { content: { search: query } },
572
+ ],
573
+ },
574
+ orderBy: {
575
+ _relevance: {
576
+ fields: ['title', 'content'],
577
+ search: query,
578
+ sort: 'desc',
579
+ },
580
+ },
581
+ });
582
+ }
583
+
584
+ // Aggregations
585
+ async getPostStats() {
586
+ return prisma.post.aggregate({
587
+ _count: { id: true },
588
+ _avg: { id: true },
589
+ where: { published: true },
590
+ });
591
+ }
592
+
593
+ // Group by
594
+ async getPostCountByAuthor() {
595
+ return prisma.post.groupBy({
596
+ by: ['authorId'],
597
+ _count: { id: true },
598
+ having: {
599
+ id: { _count: { gt: 5 } },
600
+ },
601
+ orderBy: {
602
+ _count: { id: 'desc' },
603
+ },
604
+ });
605
+ }
606
+
607
+ // Raw queries
608
+ async getTopAuthors(limit: number) {
609
+ return prisma.$queryRaw<Array<{ author_id: string; post_count: bigint }>>`
610
+ SELECT author_id, COUNT(*) as post_count
611
+ FROM posts
612
+ WHERE published = true
613
+ GROUP BY author_id
614
+ ORDER BY post_count DESC
615
+ LIMIT ${limit}
616
+ `;
617
+ }
618
+
619
+ // Complex filtering
620
+ async findPosts(filters: {
621
+ categoryIds?: string[];
622
+ tagIds?: string[];
623
+ authorId?: string;
624
+ published?: boolean;
625
+ search?: string;
626
+ dateRange?: { start: Date; end: Date };
627
+ }) {
628
+ const where: Prisma.PostWhereInput = {};
629
+
630
+ if (filters.categoryIds?.length) {
631
+ where.categories = {
632
+ some: { id: { in: filters.categoryIds } },
633
+ };
634
+ }
635
+
636
+ if (filters.tagIds?.length) {
637
+ where.tags = {
638
+ some: { id: { in: filters.tagIds } },
639
+ };
640
+ }
641
+
642
+ if (filters.authorId) {
643
+ where.authorId = filters.authorId;
644
+ }
645
+
646
+ if (filters.published !== undefined) {
647
+ where.published = filters.published;
648
+ }
649
+
650
+ if (filters.search) {
651
+ where.OR = [
652
+ { title: { contains: filters.search, mode: 'insensitive' } },
653
+ { content: { contains: filters.search, mode: 'insensitive' } },
654
+ ];
655
+ }
656
+
657
+ if (filters.dateRange) {
658
+ where.createdAt = {
659
+ gte: filters.dateRange.start,
660
+ lte: filters.dateRange.end,
661
+ };
662
+ }
663
+
664
+ return prisma.post.findMany({
665
+ where,
666
+ include: {
667
+ author: { include: { profile: true } },
668
+ categories: true,
669
+ tags: true,
670
+ },
671
+ orderBy: { createdAt: 'desc' },
672
+ });
673
+ }
674
+ }
675
+ ```
676
+
677
+ ### 6. Connection and Configuration
678
+
679
+ ```typescript
680
+ // src/lib/prisma.ts
681
+ import { PrismaClient } from '@prisma/client';
682
+
683
+ declare global {
684
+ var prisma: PrismaClient | undefined;
685
+ }
686
+
687
+ const prismaClientSingleton = () => {
688
+ return new PrismaClient({
689
+ log: process.env.NODE_ENV === 'development'
690
+ ? ['query', 'error', 'warn']
691
+ : ['error'],
692
+ });
693
+ };
694
+
695
+ export const prisma = globalThis.prisma ?? prismaClientSingleton();
696
+
697
+ if (process.env.NODE_ENV !== 'production') {
698
+ globalThis.prisma = prisma;
699
+ }
700
+
701
+ // With middleware for soft delete
702
+ const prismaWithSoftDelete = new PrismaClient().$extends({
703
+ query: {
704
+ user: {
705
+ async findMany({ model, operation, args, query }) {
706
+ args.where = { ...args.where, deletedAt: null };
707
+ return query(args);
708
+ },
709
+ async delete({ model, operation, args, query }) {
710
+ return prisma.user.update({
711
+ ...args,
712
+ data: { deletedAt: new Date() },
713
+ });
714
+ },
715
+ },
716
+ },
42
717
  });
718
+ ```
43
719
 
44
- // Transaction
45
- await prisma.$transaction([
46
- prisma.user.create({ data: userData }),
47
- prisma.post.create({ data: postData })
48
- ]);
720
+ ## Use Cases
721
+
722
+ ### Pagination with Cursor
723
+
724
+ ```typescript
725
+ async function getPaginatedPosts(cursor?: string, take: number = 20) {
726
+ const posts = await prisma.post.findMany({
727
+ take: take + 1,
728
+ ...(cursor && {
729
+ skip: 1,
730
+ cursor: { id: cursor },
731
+ }),
732
+ orderBy: { createdAt: 'desc' },
733
+ include: { author: true },
734
+ });
735
+
736
+ const hasMore = posts.length > take;
737
+ const data = hasMore ? posts.slice(0, -1) : posts;
738
+ const nextCursor = hasMore ? data[data.length - 1].id : null;
739
+
740
+ return { data, nextCursor, hasMore };
741
+ }
742
+ ```
743
+
744
+ ### Optimistic Concurrency
745
+
746
+ ```typescript
747
+ async function updateWithVersion(postId: string, data: any, version: number) {
748
+ const result = await prisma.post.updateMany({
749
+ where: {
750
+ id: postId,
751
+ version: version,
752
+ },
753
+ data: {
754
+ ...data,
755
+ version: { increment: 1 },
756
+ },
757
+ });
758
+
759
+ if (result.count === 0) {
760
+ throw new Error('Concurrent modification detected');
761
+ }
762
+
763
+ return prisma.post.findUnique({ where: { id: postId } });
764
+ }
49
765
  ```
50
766
 
51
767
  ## Best Practices
52
- - Use migrations
53
- - Use transactions
54
- - Include only needed relations
55
- - Use select for partial data
768
+
769
+ ### Do's
770
+
771
+ - Use type-safe queries with generated types
772
+ - Implement proper error handling
773
+ - Use transactions for multi-operation writes
774
+ - Create indexes for frequently queried fields
775
+ - Use select/include to limit returned data
776
+ - Implement pagination for large datasets
777
+ - Use connection pooling in production
778
+ - Run migrations in CI/CD pipelines
779
+ - Seed development databases
780
+ - Use Prisma Studio for debugging
781
+
782
+ ### Don'ts
783
+
784
+ - Don't expose Prisma Client directly in APIs
785
+ - Don't skip migrations in production
786
+ - Don't use raw queries unless necessary
787
+ - Don't ignore relation loading (N+1)
788
+ - Don't hardcode connection strings
789
+ - Don't skip input validation
790
+ - Don't use implicit many-to-many for complex relations
791
+ - Don't ignore transaction isolation levels
792
+ - Don't skip database backups
793
+ - Don't use synchronous operations
794
+
795
+ ## References
796
+
797
+ - [Prisma Documentation](https://www.prisma.io/docs)
798
+ - [Prisma Client API](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference)
799
+ - [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference)
800
+ - [Prisma Best Practices](https://www.prisma.io/docs/guides)
801
+ - [Prisma Blog](https://www.prisma.io/blog)