home / skills / physics91 / claude-vibe / orm-reviewer
This skill helps review ORM code for query efficiency, loading strategies, transactions, and N+1 prevention to improve data access.
npx playbooks add skill physics91/claude-vibe --skill orm-reviewerReview the files below or copy the command above to add this skill to your agents.
---
name: orm-reviewer
description: |
WHEN: ORM code review, Prisma/TypeORM/SQLAlchemy/GORM patterns, lazy loading, transactions
WHAT: Query efficiency + Lazy/eager loading + Transaction handling + N+1 prevention + Model design
WHEN NOT: Raw SQL → sql-optimizer, Schema design → schema-reviewer
---
# ORM Reviewer Skill
## Purpose
Reviews ORM code for query efficiency, loading strategies, transactions, and best practices.
## When to Use
- ORM code review
- Prisma/TypeORM/SQLAlchemy patterns
- N+1 query detection
- Transaction handling review
- Model relationship design
## Project Detection
- `schema.prisma` (Prisma)
- `@Entity` decorators (TypeORM)
- `models.py` (Django/SQLAlchemy)
- GORM struct tags
## Workflow
### Step 1: Analyze ORM
```
**ORM**: Prisma / TypeORM / SQLAlchemy
**Database**: PostgreSQL
**Models**: 15
**Relationships**: HasMany, BelongsTo, ManyToMany
```
### Step 2: Select Review Areas
**AskUserQuestion:**
```
"Which areas to review?"
Options:
- Full ORM review (recommended)
- Query efficiency
- Loading strategies (N+1)
- Transaction handling
- Model design
multiSelect: true
```
## Detection Rules
### Prisma Patterns
#### N+1 Prevention
```typescript
// BAD: N+1 queries
const users = await prisma.user.findMany();
for (const user of users) {
const orders = await prisma.order.findMany({
where: { userId: user.id }
});
// N+1 queries!
}
// GOOD: Include related data
const users = await prisma.user.findMany({
include: {
orders: true
}
});
// GOOD: Selective include
const users = await prisma.user.findMany({
include: {
orders: {
where: { status: 'pending' },
take: 5,
orderBy: { createdAt: 'desc' }
}
}
});
```
#### Select Optimization
```typescript
// BAD: Fetching all fields
const users = await prisma.user.findMany();
// GOOD: Select only needed fields
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true
}
});
// GOOD: Combining select with relations
const orders = await prisma.order.findMany({
select: {
id: true,
total: true,
user: {
select: {
name: true,
email: true
}
}
}
});
```
#### Transactions
```typescript
// BAD: No transaction for related operations
await prisma.order.create({ data: orderData });
await prisma.orderItem.createMany({ data: items });
await prisma.inventory.updateMany({ ... });
// If inventory update fails, order is orphaned!
// GOOD: Interactive transaction
const result = await prisma.$transaction(async (tx) => {
const order = await tx.order.create({ data: orderData });
await tx.orderItem.createMany({
data: items.map(item => ({ ...item, orderId: order.id }))
});
await tx.inventory.updateMany({
where: { productId: { in: items.map(i => i.productId) } },
data: { quantity: { decrement: 1 } }
});
return order;
});
```
### TypeORM Patterns
#### Eager vs Lazy Loading
```typescript
// BAD: Lazy loading causing N+1
@Entity()
class User {
@OneToMany(() => Order, order => order.user)
orders: Order[]; // Lazy by default
}
// Triggers N+1:
const users = await userRepo.find();
for (const user of users) {
console.log(user.orders); // Each access = query!
}
// GOOD: Eager loading with relations
const users = await userRepo.find({
relations: ['orders']
});
// GOOD: QueryBuilder for complex queries
const users = await userRepo
.createQueryBuilder('user')
.leftJoinAndSelect('user.orders', 'order')
.where('order.status = :status', { status: 'pending' })
.getMany();
```
#### Query Optimization
```typescript
// BAD: Multiple queries
const user = await userRepo.findOne({ where: { id } });
const orders = await orderRepo.find({ where: { userId: id } });
const reviews = await reviewRepo.find({ where: { userId: id } });
// GOOD: Single query with joins
const user = await userRepo.findOne({
where: { id },
relations: ['orders', 'reviews']
});
// GOOD: Select specific columns
const users = await userRepo.find({
select: ['id', 'name', 'email'],
where: { status: 'active' }
});
```
### SQLAlchemy Patterns
#### Loading Strategies
```python
# BAD: Lazy loading N+1
users = session.query(User).all()
for user in users:
print(user.orders) # N queries!
# GOOD: Eager loading
from sqlalchemy.orm import joinedload, selectinload
# For one-to-many: selectinload (2 queries)
users = session.query(User).options(
selectinload(User.orders)
).all()
# For many-to-one: joinedload (1 query)
orders = session.query(Order).options(
joinedload(Order.user)
).all()
# Combined
users = session.query(User).options(
selectinload(User.orders).selectinload(Order.items),
joinedload(User.profile)
).all()
```
#### Session Management
```python
# BAD: Long-lived session
session = Session()
# ... many operations over time
session.close() # Objects may be stale
# GOOD: Context manager
with Session() as session:
user = session.query(User).first()
# Session auto-closes
# GOOD: Scoped session in web apps
from sqlalchemy.orm import scoped_session
Session = scoped_session(sessionmaker(bind=engine))
# In request handler
def handle_request():
try:
# Use Session()
Session.commit()
except:
Session.rollback()
raise
finally:
Session.remove() # Clean up for this thread
```
### Django ORM Patterns
#### N+1 Prevention
```python
# BAD: N+1 queries
orders = Order.objects.all()
for order in orders:
print(order.user.name) # N queries!
# GOOD: select_related for ForeignKey
orders = Order.objects.select_related('user').all()
# GOOD: prefetch_related for reverse FK / M2M
users = User.objects.prefetch_related('orders').all()
# GOOD: Combined with Prefetch for filtering
from django.db.models import Prefetch
users = User.objects.prefetch_related(
Prefetch(
'orders',
queryset=Order.objects.filter(status='pending')
)
).all()
```
#### Query Optimization
```python
# BAD: .count() after .all()
count = len(Order.objects.all()) # Loads all objects!
# GOOD: Database count
count = Order.objects.count()
# BAD: Multiple queries
user = User.objects.get(id=user_id)
order_count = Order.objects.filter(user=user).count()
# GOOD: Annotation
from django.db.models import Count
user = User.objects.annotate(
order_count=Count('orders')
).get(id=user_id)
# values() for specific columns only
emails = User.objects.values_list('email', flat=True)
```
### GORM (Go) Patterns
```go
// BAD: N+1 queries
var users []User
db.Find(&users)
for _, user := range users {
var orders []Order
db.Where("user_id = ?", user.ID).Find(&orders)
}
// GOOD: Preload
var users []User
db.Preload("Orders").Find(&users)
// GOOD: Preload with conditions
db.Preload("Orders", "status = ?", "pending").Find(&users)
// GOOD: Nested preload
db.Preload("Orders.Items").Find(&users)
// Select specific columns
var users []User
db.Select("id", "name", "email").Find(&users)
```
## Response Template
```
## ORM Code Review Results
**ORM**: Prisma
**Database**: PostgreSQL
**Models**: 12
### N+1 Queries
| Status | File | Issue |
|--------|------|-------|
| CRITICAL | userService.ts:45 | Loop with findMany causing N+1 |
### Loading Strategy
| Status | File | Issue |
|--------|------|-------|
| HIGH | orderController.ts:23 | Missing include for user relation |
### Transactions
| Status | File | Issue |
|--------|------|-------|
| HIGH | checkoutService.ts:67 | Related operations without transaction |
### Query Efficiency
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | reportService.ts:12 | Selecting all columns |
### Recommended Fixes
```typescript
// Fix N+1 in userService.ts
const users = await prisma.user.findMany({
include: { orders: true }
});
// Add transaction in checkoutService.ts
await prisma.$transaction(async (tx) => {
// ... atomic operations
});
```
```
## Best Practices
1. **Loading**: Eager load known relations
2. **N+1**: Always include/preload in lists
3. **Select**: Only needed columns
4. **Transactions**: Wrap related operations
5. **Batching**: Use createMany, bulk operations
## Integration
- `sql-optimizer`: Raw query optimization
- `schema-reviewer`: Model design
- `perf-analyzer`: Application performance
This skill reviews ORM code for query efficiency, loading strategies, transaction safety, and model relationship patterns across Prisma, TypeORM, SQLAlchemy, Django ORM, and GORM. It highlights N+1 query sources, recommends eager vs lazy loading, checks transaction boundaries, and suggests model-level optimizations to reduce round-trips and data transfer. The goal is concrete, actionable fixes developers can apply rapidly.
The skill scans project files and idioms (e.g., schema.prisma, @Entity annotations, models.py, GORM struct tags) to detect ORM usage. It identifies patterns that cause N+1 queries, unnecessary full-row selects, missing transactions around multi-step operations, and suboptimal loading strategies, then produces prioritized findings with code-level fix suggestions. It focuses on runtime query behavior and usage patterns rather than raw SQL or overall schema design.
Does this review touch raw SQL queries?
No. The skill focuses on ORM usage patterns and generated queries; raw SQL optimization is better handled by a dedicated SQL optimizer tool.
Will it redesign the database schema?
No. It recommends model and relation usage improvements but avoids full schema redesigns; use a schema reviewer for structural changes.