Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:22:25 +08:00
commit c3294f28aa
60 changed files with 10297 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
# Prisma 6 Breaking Changes - Detailed Reference
## 1. Buffer → Uint8Array
**Before (Prisma 5):**
```typescript
const user = await prisma.user.create({
data: {
name: 'Alice',
data: Buffer.from('hello', 'utf-8')
}
})
const text = user.data.toString('utf-8')
```
**After (Prisma 6):**
```typescript
const encoder = new TextEncoder()
const decoder = new TextDecoder()
const user = await prisma.user.create({
data: {
name: 'Alice',
data: encoder.encode('hello')
}
})
const text = decoder.decode(user.data)
```
**Type Changes:**
- Schema `Bytes` type now maps to `Uint8Array` instead of `Buffer`
- All database binary data returned as `Uint8Array`
- `Buffer` methods no longer available on Bytes fields
**Migration Steps:**
1. Find all Buffer operations: `grep -r "Buffer.from\|\.toString(" --include="*.ts" --include="*.js"`
2. Replace with TextEncoder/TextDecoder
3. Update type annotations: `Buffer``Uint8Array`
## 2. Implicit Many-to-Many Primary Keys
**Before (Prisma 5):**
Implicit m-n join tables had auto-generated integer primary keys.
**After (Prisma 6):**
Implicit m-n join tables use compound primary keys based on foreign keys.
**Example Schema:**
```prisma
model Post {
id Int @id @default(autoincrement())
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
posts Post[]
}
```
**Migration Impact:**
- Prisma generates `_CategoryToPost` join table
- **Prisma 5**: PK was auto-increment `id`
- **Prisma 6**: PK is compound `(A, B)` where A/B are foreign keys
**Migration:**
```sql
ALTER TABLE "_CategoryToPost" DROP CONSTRAINT "_CategoryToPost_pkey";
ALTER TABLE "_CategoryToPost" ADD CONSTRAINT "_CategoryToPost_AB_pkey" PRIMARY KEY ("A", "B");
```
This migration is auto-generated when running `prisma migrate dev` after upgrading.
**Action Required:**
- Run migration in development
- Review generated SQL before production deploy
- No code changes needed (Prisma Client handles internally)
## 3. NotFoundError → P2025 Error Code
**Before (Prisma 5):**
```typescript
import { PrismaClient, NotFoundError } from '@prisma/client'
try {
const user = await prisma.user.delete({
where: { id: 999 }
})
} catch (error) {
if (error instanceof NotFoundError) {
console.log('User not found')
}
}
```
**After (Prisma 6):**
```typescript
import { PrismaClient, Prisma } from '@prisma/client'
try {
const user = await prisma.user.delete({
where: { id: 999 }
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2025') {
console.log('User not found')
}
}
}
```
**Type Guard Pattern:**
```typescript
function isNotFoundError(error: unknown): boolean {
return (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2025'
)
}
try {
const user = await prisma.user.delete({ where: { id: 999 } })
} catch (error) {
if (isNotFoundError(error)) {
console.log('User not found')
}
throw error
}
```
**Migration Steps:**
1. Find all NotFoundError usage: `grep -r "NotFoundError" --include="*.ts"`
2. Remove NotFoundError imports
3. Replace error class checks with P2025 code checks
4. Use `Prisma.PrismaClientKnownRequestError` type guard
## 4. Reserved Keywords
**Breaking Change:**
The following field/model names are now reserved:
- `async`
- `await`
- `using`
**Before (Prisma 5):**
```prisma
model Task {
id Int @id @default(autoincrement())
async Boolean
}
```
**After (Prisma 6):**
```prisma
model Task {
id Int @id @default(autoincrement())
isAsync Boolean @map("async")
}
```
**Migration Steps:**
1. Find reserved keywords in schema: `grep -E "^\s*(async|await|using)\s" schema.prisma`
2. Rename fields/models with descriptive alternatives
3. Use `@map()` to maintain database column names
4. Update all application code references
**Recommended Renames:**
- `async``isAsync`, `asyncMode`, `asynchronous`
- `await``awaitStatus`, `pending`, `waitingFor`
- `using``inUse`, `isActive`, `usage`

View File

@@ -0,0 +1,72 @@
# Prisma 6 Migration Checklist
## Pre-Migration
- [ ] Backup production database
- [ ] Create feature branch for migration
- [ ] Run existing tests to establish baseline
- [ ] Document current Prisma version
## Schema Assessment
- [ ] Search for Bytes fields: `grep "Bytes" prisma/schema.prisma`
- [ ] Search for implicit m-n relations (no explicit join table)
- [ ] Search for reserved keywords: `grep -E "^\s*(async|await|using)\s" prisma/schema.prisma`
- [ ] List all models and relations
## Code Assessment
- [ ] Find Buffer usage: `grep -r "Buffer\\.from\\|Buffer\\.alloc" --include="*.ts"`
- [ ] Find toString on Bytes: `grep -r "\\.toString(" --include="*.ts"`
- [ ] Find NotFoundError: `grep -r "NotFoundError" --include="*.ts"`
- [ ] Document all locations requiring changes
## Update Dependencies
- [ ] Update package.json: `npm install prisma@6 @prisma/client@6`
- [ ] Regenerate client: `npx prisma generate`
- [ ] Verify TypeScript errors appear (expected)
## Schema Migration
- [ ] Rename any reserved keyword fields/models
- [ ] Add `@map()` to maintain database compatibility
- [ ] Run `npx prisma migrate dev --name v6-upgrade`
- [ ] Review generated migration SQL
- [ ] Test migration on development database
## Code Updates: Buffer → Uint8Array
- [ ] Create TextEncoder/TextDecoder instances
- [ ] Replace `Buffer.from(str, 'utf-8')` with `encoder.encode(str)`
- [ ] Replace `buffer.toString('utf-8')` with `decoder.decode(uint8array)`
- [ ] Update type annotations: `Buffer``Uint8Array`
- [ ] Handle edge cases (binary data, non-UTF8 encodings)
## Code Updates: NotFoundError → P2025
- [ ] Remove `NotFoundError` imports
- [ ] Replace `error instanceof NotFoundError` with P2025 checks
- [ ] Import `Prisma` from '@prisma/client'
- [ ] Use `Prisma.PrismaClientKnownRequestError` type guard
- [ ] Create helper functions for common error checks
## Testing
- [ ] Run TypeScript compiler: `npx tsc --noEmit`
- [ ] Fix any remaining type errors
- [ ] Run unit tests
- [ ] Run integration tests
- [ ] Test Bytes field operations manually
- [ ] Test not-found error handling
- [ ] Test implicit m-n queries
## Production Deployment
- [ ] Review migration SQL one final time
- [ ] Plan maintenance window if needed
- [ ] Deploy migration: `npx prisma migrate deploy`
- [ ] Deploy application code
- [ ] Monitor error logs for issues
- [ ] Verify Bytes operations work correctly
- [ ] Rollback plan ready if needed

View File

@@ -0,0 +1,193 @@
# Prisma 6 Migration Examples
## Example 1: Complete Bytes Field Migration
**Schema:**
```prisma
model Document {
id Int @id @default(autoincrement())
content Bytes
}
```
**Before (Prisma 5):**
```typescript
const doc = await prisma.document.create({
data: {
content: Buffer.from('Important document content', 'utf-8')
}
})
console.log(doc.content.toString('utf-8'))
```
**After (Prisma 6):**
```typescript
const encoder = new TextEncoder()
const decoder = new TextDecoder()
const doc = await prisma.document.create({
data: {
content: encoder.encode('Important document content')
}
})
console.log(decoder.decode(doc.content))
```
**Binary Data (non-text):**
```typescript
const binaryData = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f])
const doc = await prisma.document.create({
data: {
content: binaryData
}
})
const retrieved = await prisma.document.findUnique({ where: { id: doc.id } })
console.log(retrieved.content)
```
## Example 2: NotFoundError Migration
**Before (Prisma 5):**
```typescript
import { PrismaClient, NotFoundError } from '@prisma/client'
async function deleteUser(id: number) {
try {
const user = await prisma.user.delete({ where: { id } })
return { success: true, user }
} catch (error) {
if (error instanceof NotFoundError) {
return { success: false, error: 'User not found' }
}
throw error
}
}
```
**After (Prisma 6):**
```typescript
import { PrismaClient, Prisma } from '@prisma/client'
async function deleteUser(id: number) {
try {
const user = await prisma.user.delete({ where: { id } })
return { success: true, user }
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2025') {
return { success: false, error: 'User not found' }
}
}
throw error
}
}
```
**Reusable Helper:**
```typescript
import { Prisma } from '@prisma/client'
export function isPrismaNotFoundError(
error: unknown
): error is Prisma.PrismaClientKnownRequestError {
return (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2025'
)
}
async function deleteUser(id: number) {
try {
const user = await prisma.user.delete({ where: { id } })
return { success: true, user }
} catch (error) {
if (isPrismaNotFoundError(error)) {
return { success: false, error: 'User not found' }
}
throw error
}
}
```
## Example 3: Reserved Keyword Migration
**Before (Prisma 5):**
```prisma
model Task {
id Int @id @default(autoincrement())
async Boolean
await String?
}
```
**After (Prisma 6):**
```prisma
model Task {
id Int @id @default(autoincrement())
isAsync Boolean @map("async")
awaitMsg String? @map("await")
}
```
**Code Update:**
```typescript
const task = await prisma.task.create({
data: {
isAsync: true,
awaitMsg: 'Waiting for completion'
}
})
console.log(task.isAsync)
console.log(task.awaitMsg)
```
**Database columns remain unchanged** (`async`, `await`), but TypeScript code uses new names.
## Example 4: Implicit Many-to-Many Migration
**Schema:**
```prisma
model Post {
id Int @id @default(autoincrement())
title String
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}
```
**Auto-Generated Migration:**
```sql
ALTER TABLE "_CategoryToPost" DROP CONSTRAINT "_CategoryToPost_pkey";
ALTER TABLE "_CategoryToPost" ADD CONSTRAINT "_CategoryToPost_AB_pkey"
PRIMARY KEY ("A", "B");
```
**No code changes needed**:
```typescript
const post = await prisma.post.create({
data: {
title: 'Hello World',
categories: {
connect: [{ id: 1 }, { id: 2 }]
}
}
})
const postWithCategories = await prisma.post.findUnique({
where: { id: post.id },
include: { categories: true }
})
```
**Migration runs automatically** when you run `npx prisma migrate dev` after upgrading to Prisma 6.

View File

@@ -0,0 +1,45 @@
# Prisma 6 Migration Troubleshooting
## Issue: Type error on Bytes field
**Error:**
```
Type 'Buffer' is not assignable to type 'Uint8Array'
```
**Solution:**
Replace Buffer operations with TextEncoder/TextDecoder or use Uint8Array directly.
## Issue: Migration fails with duplicate key
**Error:**
```
ERROR: duplicate key value violates unique constraint "_CategoryToPost_AB_unique"
```
**Solution:**
Implicit m-n tables may have duplicate entries. Clean data before migration:
```sql
DELETE FROM "_CategoryToPost" a USING "_CategoryToPost" b
WHERE a.ctid < b.ctid AND a."A" = b."A" AND a."B" = b."B";
```
## Issue: NotFoundError import fails
**Error:**
```
Module '"@prisma/client"' has no exported member 'NotFoundError'
```
**Solution:**
Remove NotFoundError import, use P2025 error code checking instead.
## Issue: Reserved keyword compilation error
**Error:**
```
'async' is a reserved word
```
**Solution:**
Rename field in schema with `@map()` to preserve database column name.