Initial commit: The Ultimate Antigravity Skills Collection (58 Skills)
This commit is contained in:
224
skills/backend-dev-guidelines/resources/database-patterns.md
Normal file
224
skills/backend-dev-guidelines/resources/database-patterns.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Database Patterns - Prisma Best Practices
|
||||
|
||||
Complete guide to database access patterns using Prisma in backend microservices.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [PrismaService Usage](#prismaservice-usage)
|
||||
- [Repository Pattern](#repository-pattern)
|
||||
- [Transaction Patterns](#transaction-patterns)
|
||||
- [Query Optimization](#query-optimization)
|
||||
- [N+1 Query Prevention](#n1-query-prevention)
|
||||
- [Error Handling](#error-handling)
|
||||
|
||||
---
|
||||
|
||||
## PrismaService Usage
|
||||
|
||||
### Basic Pattern
|
||||
|
||||
```typescript
|
||||
import { PrismaService } from '@project-lifecycle-portal/database';
|
||||
|
||||
// Always use PrismaService.main
|
||||
const users = await PrismaService.main.user.findMany();
|
||||
```
|
||||
|
||||
### Check Availability
|
||||
|
||||
```typescript
|
||||
if (!PrismaService.isAvailable) {
|
||||
throw new Error('Prisma client not initialized');
|
||||
}
|
||||
|
||||
const user = await PrismaService.main.user.findUnique({ where: { id } });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Repository Pattern
|
||||
|
||||
### Why Use Repositories
|
||||
|
||||
✅ **Use repositories when:**
|
||||
- Complex queries with joins/includes
|
||||
- Query used in multiple places
|
||||
- Need caching layer
|
||||
- Want to mock for testing
|
||||
|
||||
❌ **Skip repositories for:**
|
||||
- Simple one-off queries
|
||||
- Prototyping (can refactor later)
|
||||
|
||||
### Repository Template
|
||||
|
||||
```typescript
|
||||
export class UserRepository {
|
||||
async findById(id: string): Promise<User | null> {
|
||||
return PrismaService.main.user.findUnique({
|
||||
where: { id },
|
||||
include: { profile: true },
|
||||
});
|
||||
}
|
||||
|
||||
async findActive(): Promise<User[]> {
|
||||
return PrismaService.main.user.findMany({
|
||||
where: { isActive: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.UserCreateInput): Promise<User> {
|
||||
return PrismaService.main.user.create({ data });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transaction Patterns
|
||||
|
||||
### Simple Transaction
|
||||
|
||||
```typescript
|
||||
const result = await PrismaService.main.$transaction(async (tx) => {
|
||||
const user = await tx.user.create({ data: userData });
|
||||
const profile = await tx.userProfile.create({ data: { userId: user.id } });
|
||||
return { user, profile };
|
||||
});
|
||||
```
|
||||
|
||||
### Interactive Transaction
|
||||
|
||||
```typescript
|
||||
const result = await PrismaService.main.$transaction(
|
||||
async (tx) => {
|
||||
const user = await tx.user.findUnique({ where: { id } });
|
||||
if (!user) throw new Error('User not found');
|
||||
|
||||
return await tx.user.update({
|
||||
where: { id },
|
||||
data: { lastLogin: new Date() },
|
||||
});
|
||||
},
|
||||
{
|
||||
maxWait: 5000,
|
||||
timeout: 10000,
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Optimization
|
||||
|
||||
### Use select to Limit Fields
|
||||
|
||||
```typescript
|
||||
// ❌ Fetches all fields
|
||||
const users = await PrismaService.main.user.findMany();
|
||||
|
||||
// ✅ Only fetch needed fields
|
||||
const users = await PrismaService.main.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
profile: { select: { firstName: true, lastName: true } },
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Use include Carefully
|
||||
|
||||
```typescript
|
||||
// ❌ Excessive includes
|
||||
const user = await PrismaService.main.user.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
profile: true,
|
||||
posts: { include: { comments: true } },
|
||||
workflows: { include: { steps: { include: { actions: true } } } },
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Only include what you need
|
||||
const user = await PrismaService.main.user.findUnique({
|
||||
where: { id },
|
||||
include: { profile: true },
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## N+1 Query Prevention
|
||||
|
||||
### Problem: N+1 Queries
|
||||
|
||||
```typescript
|
||||
// ❌ N+1 Query Problem
|
||||
const users = await PrismaService.main.user.findMany(); // 1 query
|
||||
|
||||
for (const user of users) {
|
||||
// N queries (one per user)
|
||||
const profile = await PrismaService.main.userProfile.findUnique({
|
||||
where: { userId: user.id },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Solution: Use include or Batching
|
||||
|
||||
```typescript
|
||||
// ✅ Single query with include
|
||||
const users = await PrismaService.main.user.findMany({
|
||||
include: { profile: true },
|
||||
});
|
||||
|
||||
// ✅ Or batch query
|
||||
const userIds = users.map(u => u.id);
|
||||
const profiles = await PrismaService.main.userProfile.findMany({
|
||||
where: { userId: { in: userIds } },
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Prisma Error Types
|
||||
|
||||
```typescript
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
try {
|
||||
await PrismaService.main.user.create({ data });
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
// Unique constraint violation
|
||||
if (error.code === 'P2002') {
|
||||
throw new ConflictError('Email already exists');
|
||||
}
|
||||
|
||||
// Foreign key constraint
|
||||
if (error.code === 'P2003') {
|
||||
throw new ValidationError('Invalid reference');
|
||||
}
|
||||
|
||||
// Record not found
|
||||
if (error.code === 'P2025') {
|
||||
throw new NotFoundError('Record not found');
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown error
|
||||
Sentry.captureException(error);
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Related Files:**
|
||||
- [SKILL.md](SKILL.md)
|
||||
- [services-and-repositories.md](services-and-repositories.md)
|
||||
- [async-and-errors.md](async-and-errors.md)
|
||||
Reference in New Issue
Block a user