omgkit 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,41 +1,16 @@
1
1
  ---
2
- name: prisma
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
2
+ name: Developing with Prisma
3
+ description: The agent implements Prisma ORM for type-safe database access with schema design, migrations, and queries. Use when building database layers, designing relational schemas, implementing type-safe queries, or managing database migrations.
12
4
  ---
13
5
 
14
- # Prisma
6
+ # Developing with Prisma
15
7
 
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
8
+ ## Quick Start
33
9
 
34
10
  ```prisma
35
11
  // prisma/schema.prisma
36
12
  generator client {
37
- provider = "prisma-client-js"
38
- previewFeatures = ["fullTextSearch", "fullTextIndex"]
13
+ provider = "prisma-client-js"
39
14
  }
40
15
 
41
16
  datasource db {
@@ -43,759 +18,93 @@ datasource db {
43
18
  url = env("DATABASE_URL")
44
19
  }
45
20
 
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
62
21
  model User {
63
22
  id String @id @default(cuid())
64
23
  email String @unique
65
- password String
66
- role UserRole @default(USER)
67
- isActive Boolean @default(true)
24
+ posts Post[]
68
25
  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
26
  @@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")
95
27
  }
96
28
 
97
- // One-to-many relation
98
29
  model Post {
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
-
30
+ id String @id @default(cuid())
31
+ title String
108
32
  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")
33
+ author User @relation(fields: [authorId], references: [id])
211
34
  }
212
35
  ```
213
36
 
214
- ### 2. Type-Safe Queries
215
-
216
- ```typescript
217
- // src/repositories/user.repository.ts
218
- import { PrismaClient, Prisma, User, UserRole } from '@prisma/client';
219
-
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[] = [];
383
-
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
37
  ```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
38
+ npx prisma migrate dev --name init
487
39
  npx prisma generate
488
-
489
- # Open Prisma Studio
490
- npx prisma studio
491
40
  ```
492
41
 
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
- });
42
+ ## Features
542
43
 
543
- console.log({ admin, categories, products });
544
- }
44
+ | Feature | Description | Guide |
45
+ |---------|-------------|-------|
46
+ | Schema Design | Declarative data modeling with relations | Define models, relations, indexes in schema.prisma |
47
+ | Type-Safe Queries | Auto-generated TypeScript types | Use `findMany`, `findUnique`, `create`, `update` |
48
+ | Migrations | Version-controlled schema changes | `prisma migrate dev` for development, `deploy` for production |
49
+ | Relations | One-to-one, one-to-many, many-to-many | Use `include` or `select` to load related data |
50
+ | Transactions | ACID operations across multiple queries | Use `$transaction` for atomic operations |
51
+ | Raw Queries | Execute raw SQL when needed | Use `$queryRaw` for complex queries |
545
52
 
546
- main()
547
- .catch((e) => {
548
- console.error(e);
549
- process.exit(1);
550
- })
551
- .finally(async () => {
552
- await prisma.$disconnect();
553
- });
554
- ```
53
+ ## Common Patterns
555
54
 
556
- ### 5. Advanced Queries
55
+ ### Repository with Pagination
557
56
 
558
57
  ```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
- }
58
+ async function findUsers(page = 1, limit = 20, where?: Prisma.UserWhereInput) {
59
+ const [data, total] = await prisma.$transaction([
60
+ prisma.user.findMany({ where, skip: (page - 1) * limit, take: limit, include: { profile: true } }),
61
+ prisma.user.count({ where }),
62
+ ]);
63
+ return { data, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } };
674
64
  }
675
65
  ```
676
66
 
677
- ### 6. Connection and Configuration
67
+ ### Interactive Transaction
678
68
 
679
69
  ```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'],
70
+ async function createOrder(userId: string, items: { productId: string; qty: number }[]) {
71
+ return prisma.$transaction(async (tx) => {
72
+ let total = 0;
73
+ for (const item of items) {
74
+ const product = await tx.product.update({
75
+ where: { id: item.productId },
76
+ data: { stock: { decrement: item.qty } },
77
+ });
78
+ if (product.stock < 0) throw new Error(`Insufficient stock: ${product.name}`);
79
+ total += product.price * item.qty;
80
+ }
81
+ return tx.order.create({ data: { userId, total, items: { create: items } } });
692
82
  });
693
- };
694
-
695
- export const prisma = globalThis.prisma ?? prismaClientSingleton();
696
-
697
- if (process.env.NODE_ENV !== 'production') {
698
- globalThis.prisma = prisma;
699
83
  }
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
- },
717
- });
718
84
  ```
719
85
 
720
- ## Use Cases
721
-
722
- ### Pagination with Cursor
86
+ ### Cursor-Based Pagination
723
87
 
724
88
  ```typescript
725
- async function getPaginatedPosts(cursor?: string, take: number = 20) {
89
+ async function getPaginatedPosts(cursor?: string, take = 20) {
726
90
  const posts = await prisma.post.findMany({
727
91
  take: take + 1,
728
- ...(cursor && {
729
- skip: 1,
730
- cursor: { id: cursor },
731
- }),
92
+ ...(cursor && { skip: 1, cursor: { id: cursor } }),
732
93
  orderBy: { createdAt: 'desc' },
733
- include: { author: true },
734
94
  });
735
-
736
95
  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 } });
96
+ return { data: hasMore ? posts.slice(0, -1) : posts, nextCursor: hasMore ? posts[take - 1].id : null };
764
97
  }
765
98
  ```
766
99
 
767
100
  ## Best Practices
768
101
 
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)
102
+ | Do | Avoid |
103
+ |----|-------|
104
+ | Use `select` to fetch only needed fields | Exposing Prisma Client directly in APIs |
105
+ | Create indexes for frequently queried fields | Skipping migrations in production |
106
+ | Use transactions for multi-table operations | Ignoring N+1 query problems |
107
+ | Run migrations in CI/CD pipelines | Hardcoding connection strings |
108
+ | Use connection pooling in production | Using raw queries unless necessary |
109
+ | Validate input before database operations | Using implicit many-to-many for complex joins |
110
+ | Seed development databases consistently | Ignoring transaction isolation levels |