447 lines
9.7 KiB
Markdown
447 lines
9.7 KiB
Markdown
# Example Reviews
|
||
|
||
Complete code review examples showing typical findings and recommendations.
|
||
|
||
---
|
||
|
||
## Example 1: E-commerce API (Next.js)
|
||
|
||
**Context:** Next.js 14 App Router with Prisma, deployed to Vercel
|
||
|
||
**Project Structure:**
|
||
```
|
||
app/
|
||
├── api/
|
||
│ ├── products/route.ts
|
||
│ ├── users/route.ts
|
||
│ └── search/route.ts
|
||
├── lib/
|
||
│ └── db.ts
|
||
└── .env
|
||
```
|
||
|
||
**Findings:**
|
||
|
||
```
|
||
Prisma Code Review - E-commerce API
|
||
Generated: 2025-11-21
|
||
Files Reviewed: 15
|
||
|
||
CRITICAL Issues (P0): 2
|
||
HIGH Issues (P1): 1
|
||
MEDIUM Issues (P2): 3
|
||
LOW Issues (P3): 2
|
||
|
||
Overall Assessment: CRITICAL ISSUES - Do not deploy
|
||
|
||
---
|
||
|
||
[P0] Multiple PrismaClient Instances
|
||
Files:
|
||
- app/api/products/route.ts:8
|
||
- app/api/users/route.ts:12
|
||
- lib/db.ts:5
|
||
Count: 3 instances found
|
||
|
||
Code (app/api/products/route.ts:8):
|
||
```typescript
|
||
import { PrismaClient } from '@prisma/client'
|
||
const prisma = new PrismaClient()
|
||
|
||
export async function GET() {
|
||
const products = await prisma.product.findMany()
|
||
return Response.json(products)
|
||
}
|
||
```
|
||
|
||
Impact: CRITICAL - Connection pool exhaustion under load
|
||
- Each API route creates separate connection pool
|
||
- Vercel scales to 100+ concurrent functions
|
||
- 3 routes × 100 instances × 10 connections = 3000 connections!
|
||
- Database will reject connections (P1017)
|
||
|
||
Fix: Create global singleton in lib/db.ts, import everywhere
|
||
|
||
Remediation Steps:
|
||
1. Create lib/prisma.ts with global singleton pattern
|
||
2. Replace all `new PrismaClient()` with imports
|
||
3. Verify with grep (should find only 1 instance)
|
||
|
||
Reference: @prisma-6/CLIENT-singleton-pattern
|
||
|
||
---
|
||
|
||
[P0] SQL Injection Vulnerability
|
||
File: app/api/search/route.ts:23
|
||
|
||
Code:
|
||
```typescript
|
||
export async function GET(request: Request) {
|
||
const { searchParams } = new URL(request.url)
|
||
const query = searchParams.get('q')
|
||
|
||
const products = await prisma.$queryRawUnsafe(
|
||
`SELECT * FROM products WHERE name LIKE '%${query}%'`
|
||
)
|
||
|
||
return Response.json(products)
|
||
}
|
||
```
|
||
|
||
Impact: CRITICAL - Enables SQL injection attacks
|
||
- User controls `query` parameter
|
||
- Direct string interpolation allows injection
|
||
- Attacker can execute arbitrary SQL
|
||
|
||
Example attack:
|
||
```
|
||
/api/search?q=%27;%20DROP%20TABLE%20products;--
|
||
```
|
||
|
||
Fix: Use $queryRaw tagged template with automatic parameterization
|
||
|
||
Remediation:
|
||
```typescript
|
||
const products = await prisma.$queryRaw`
|
||
SELECT * FROM products WHERE name LIKE ${'%' + query + '%'}
|
||
`
|
||
```
|
||
|
||
Reference: @prisma-6/SECURITY-sql-injection
|
||
|
||
---
|
||
|
||
[P1] Missing Serverless Configuration
|
||
File: .env
|
||
|
||
Current:
|
||
```
|
||
DATABASE_URL="postgresql://user:pass@host:5432/db"
|
||
```
|
||
|
||
Impact: HIGH - Connection exhaustion in Vercel deployment
|
||
- Default pool_size = 10 connections per instance
|
||
- Vercel can scale to 100+ instances
|
||
- 100 instances × 10 connections = 1000 connections
|
||
- Most databases have 100-200 connection limit
|
||
|
||
Fix: Add ?connection_limit=1 to DATABASE_URL
|
||
|
||
Remediation:
|
||
```
|
||
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=10"
|
||
```
|
||
|
||
Why this works:
|
||
- Each Vercel function instance gets 1 connection
|
||
- 100 instances × 1 connection = 100 connections (sustainable)
|
||
- pool_timeout=10 prevents hanging on exhaustion
|
||
|
||
Reference: @prisma-6/CLIENT-serverless-config
|
||
|
||
---
|
||
|
||
[P2] Missing Input Validation
|
||
Files: app/api/users/route.ts, app/api/products/route.ts
|
||
|
||
Code (app/api/users/route.ts):
|
||
```typescript
|
||
export async function POST(request: Request) {
|
||
const data = await request.json()
|
||
const user = await prisma.user.create({ data })
|
||
return Response.json(user)
|
||
}
|
||
```
|
||
|
||
Impact: MEDIUM - Invalid data can reach database
|
||
- No validation of email format
|
||
- No validation of required fields
|
||
- Type mismatches cause runtime errors
|
||
|
||
Fix: Add Zod validation schemas before Prisma operations
|
||
|
||
Remediation:
|
||
```typescript
|
||
import { z } from 'zod'
|
||
|
||
const userSchema = z.object({
|
||
email: z.string().email(),
|
||
name: z.string().min(1),
|
||
})
|
||
|
||
export async function POST(request: Request) {
|
||
const data = await request.json()
|
||
const validated = userSchema.parse(data)
|
||
const user = await prisma.user.create({ data: validated })
|
||
return Response.json(user)
|
||
}
|
||
```
|
||
|
||
Reference: @prisma-6/SECURITY-input-validation
|
||
|
||
---
|
||
|
||
[P3] Inefficient Pagination
|
||
File: app/api/products/route.ts:15
|
||
|
||
Code:
|
||
```typescript
|
||
const page = parseInt(searchParams.get('page') ?? '0')
|
||
const products = await prisma.product.findMany({
|
||
skip: page * 100,
|
||
take: 100
|
||
})
|
||
```
|
||
|
||
Impact: LOW - Slow queries on large datasets
|
||
- Product table has 50k+ records
|
||
- Offset pagination degrades with page number
|
||
- Page 500 skips 50k records (slow!)
|
||
|
||
Fix: Use cursor-based pagination with id cursor
|
||
|
||
Remediation:
|
||
```typescript
|
||
const cursor = searchParams.get('cursor')
|
||
const products = await prisma.product.findMany({
|
||
take: 100,
|
||
cursor: cursor ? { id: cursor } : undefined,
|
||
orderBy: { id: 'asc' }
|
||
})
|
||
|
||
const nextCursor = products.length === 100
|
||
? products[99].id
|
||
: null
|
||
```
|
||
|
||
Reference: @prisma-6/QUERIES-pagination
|
||
|
||
---
|
||
|
||
RECOMMENDATION: Fix P0 issues immediately before any deployment. P1 issues will cause production failures under load.
|
||
|
||
Priority Actions:
|
||
1. Implement global singleton pattern (blocking)
|
||
2. Fix SQL injection in search endpoint (blocking)
|
||
3. Add connection_limit to DATABASE_URL (high priority)
|
||
4. Add Zod validation to API routes (recommended)
|
||
5. Optimize pagination for products (nice to have)
|
||
```
|
||
|
||
---
|
||
|
||
## Example 2: Internal Dashboard (Express)
|
||
|
||
**Context:** Express API with PostgreSQL, traditional server deployment
|
||
|
||
**Project Structure:**
|
||
```
|
||
src/
|
||
├── controllers/
|
||
│ ├── users.ts
|
||
│ └── reports.ts
|
||
├── db.ts
|
||
└── index.ts
|
||
```
|
||
|
||
**Findings:**
|
||
|
||
```
|
||
Prisma Code Review - Internal Dashboard
|
||
Generated: 2025-11-21
|
||
Files Reviewed: 8
|
||
|
||
CRITICAL Issues (P0): 0
|
||
HIGH Issues (P1): 0
|
||
MEDIUM Issues (P2): 1
|
||
LOW Issues (P3): 3
|
||
|
||
Overall Assessment: GOOD - Minor improvements recommended
|
||
|
||
---
|
||
|
||
[P2] Generic Error Handling
|
||
File: src/controllers/users.ts:45-52
|
||
|
||
Code:
|
||
```typescript
|
||
async function createUser(req: Request, res: Response) {
|
||
try {
|
||
const user = await prisma.user.create({
|
||
data: req.body
|
||
})
|
||
res.json(user)
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Database error' })
|
||
}
|
||
}
|
||
```
|
||
|
||
Impact: MEDIUM - P2002/P2025 not handled specifically
|
||
- User gets generic "Database error" for all failures
|
||
- Duplicate email returns 500 instead of 409
|
||
- Poor developer experience debugging issues
|
||
|
||
Fix: Check error.code for P2002 (unique), P2025 (not found)
|
||
|
||
Remediation:
|
||
```typescript
|
||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'
|
||
|
||
async function createUser(req: Request, res: Response) {
|
||
try {
|
||
const user = await prisma.user.create({
|
||
data: req.body
|
||
})
|
||
res.json(user)
|
||
} catch (error) {
|
||
if (error instanceof PrismaClientKnownRequestError) {
|
||
if (error.code === 'P2002') {
|
||
return res.status(409).json({
|
||
error: 'User with this email already exists'
|
||
})
|
||
}
|
||
}
|
||
res.status(500).json({ error: 'Unexpected error' })
|
||
}
|
||
}
|
||
```
|
||
|
||
Reference: @prisma-6/TRANSACTIONS-error-handling
|
||
|
||
---
|
||
|
||
[P3] Inefficient Pagination
|
||
File: src/controllers/reports.ts:78
|
||
|
||
Code:
|
||
```typescript
|
||
const reports = await prisma.report.findMany({
|
||
skip: page * 100,
|
||
take: 100,
|
||
orderBy: { createdAt: 'desc' }
|
||
})
|
||
```
|
||
|
||
Context:
|
||
- Reports table has 50k+ records
|
||
- Used in admin dashboard for audit logs
|
||
- Page 500 requires scanning 50k records
|
||
|
||
Impact: LOW - Slow queries on large datasets
|
||
- Query time increases with page number
|
||
- Database performs full table scan
|
||
- Admin dashboard feels sluggish
|
||
|
||
Fix: Use cursor-based pagination with id cursor
|
||
|
||
Remediation:
|
||
```typescript
|
||
const reports = await prisma.report.findMany({
|
||
take: 100,
|
||
cursor: lastId ? { id: lastId } : undefined,
|
||
orderBy: { createdAt: 'desc' }
|
||
})
|
||
```
|
||
|
||
Reference: @prisma-6/QUERIES-pagination
|
||
|
||
---
|
||
|
||
[P3] Missing Select Optimization
|
||
Files: 8 files with findMany() lacking select
|
||
|
||
Examples:
|
||
- src/controllers/users.ts:23
|
||
- src/controllers/reports.ts:45
|
||
- src/controllers/analytics.ts:67
|
||
|
||
Code pattern:
|
||
```typescript
|
||
const users = await prisma.user.findMany()
|
||
```
|
||
|
||
Impact: LOW - Fetching unnecessary fields
|
||
- Returns all columns including large text fields
|
||
- Increases response payload size
|
||
- Wastes database bandwidth
|
||
|
||
Fix: Add select: { id, name, email } to queries
|
||
|
||
Remediation:
|
||
```typescript
|
||
const users = await prisma.user.findMany({
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
name: true,
|
||
role: true
|
||
}
|
||
})
|
||
```
|
||
|
||
Reference: @prisma-6/QUERIES-select-optimization
|
||
|
||
---
|
||
|
||
[P3] Missing Select in List Endpoints
|
||
File: src/controllers/users.ts:88
|
||
|
||
Code:
|
||
```typescript
|
||
const users = await prisma.user.findMany({
|
||
include: { posts: true }
|
||
})
|
||
```
|
||
|
||
Impact: LOW - Over-fetching related data
|
||
- Returns ALL posts for each user
|
||
- User with 1000 posts = huge payload
|
||
- Should paginate posts separately
|
||
|
||
Fix: Limit included records or use separate query
|
||
|
||
Remediation:
|
||
```typescript
|
||
const users = await prisma.user.findMany({
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
name: true,
|
||
_count: {
|
||
select: { posts: true }
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
Reference: @prisma-6/QUERIES-select-optimization
|
||
|
||
---
|
||
|
||
ASSESSMENT: Code quality is good. No critical issues found.
|
||
|
||
Recommended Improvements:
|
||
1. Improve error handling with P-code checks (user experience)
|
||
2. Optimize pagination for reports table (performance)
|
||
3. Add select clauses to list endpoints (efficiency)
|
||
|
||
These improvements are optional but will enhance code quality and performance.
|
||
```
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
**E-commerce API:**
|
||
- High-risk serverless deployment with critical security/stability issues
|
||
- Must fix P0 issues before deployment
|
||
- Typical of AI-generated code without production hardening
|
||
|
||
**Internal Dashboard:**
|
||
- Low-risk traditional server deployment with minor optimizations
|
||
- No blocking issues
|
||
- Good baseline quality with room for improvement
|
||
|
||
Both examples demonstrate the importance of systematic code review before production deployment.
|