Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
551 lines
12 KiB
Markdown
551 lines
12 KiB
Markdown
# Prisma 7: Der TypeScript-Native ORM für 2026
|
|
|
|
**Meta-Description:** Prisma 7 Deep Dive nach dem Rust-zu-TypeScript-Rewrite. 90% kleinere Bundles, 3x schnellere Queries und verbesserte Type-Safety.
|
|
|
|
**Keywords:** Prisma, ORM, TypeScript, Database, PostgreSQL, MySQL, SQLite, Query Builder, Type Safety, Prisma 7
|
|
|
|
---
|
|
|
|
## Einführung
|
|
|
|
Prisma 7 ist ein **Game-Changer**: Das Team hat die gesamte Query Engine von Rust nach TypeScript umgeschrieben. Das Ergebnis: 90% kleinere Bundles, 3x schnellere Queries und bessere Cold-Starts für Serverless.
|
|
|
|
---
|
|
|
|
## Die Revolution: Rust-Free Prisma
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ PRISMA 6 vs PRISMA 7 │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Prisma 6 (Rust Engine): │
|
|
│ ├── Bundle Size: ~14MB (7MB gzipped) │
|
|
│ ├── Cold Start: ~300ms (Lambda) │
|
|
│ └── Query Speed: Baseline │
|
|
│ │
|
|
│ Prisma 7 (TypeScript Engine): │
|
|
│ ├── Bundle Size: ~1.6MB (600KB gzipped) → 90% kleiner │
|
|
│ ├── Cold Start: ~50ms (Lambda) → 6x schneller │
|
|
│ └── Query Speed: 3x schneller bei großen Result Sets │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Setup & Schema
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
npm install prisma @prisma/client
|
|
npx prisma init
|
|
```
|
|
|
|
### Schema Definition
|
|
|
|
```prisma
|
|
// prisma/schema.prisma
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
name String?
|
|
role Role @default(USER)
|
|
posts Post[]
|
|
profile Profile?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([email])
|
|
}
|
|
|
|
model Profile {
|
|
id String @id @default(cuid())
|
|
bio String?
|
|
avatar String?
|
|
userId String @unique
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model Post {
|
|
id String @id @default(cuid())
|
|
title String
|
|
content String?
|
|
published Boolean @default(false)
|
|
author User @relation(fields: [authorId], references: [id])
|
|
authorId String
|
|
categories Category[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([authorId])
|
|
@@index([published])
|
|
}
|
|
|
|
model Category {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
posts Post[]
|
|
}
|
|
|
|
enum Role {
|
|
USER
|
|
ADMIN
|
|
MODERATOR
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## CRUD Operations
|
|
|
|
### Create
|
|
|
|
```typescript
|
|
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
// Einzelner Datensatz
|
|
const user = await prisma.user.create({
|
|
data: {
|
|
email: 'alice@example.com',
|
|
name: 'Alice',
|
|
// Nested Create
|
|
profile: {
|
|
create: {
|
|
bio: 'Developer from Berlin'
|
|
}
|
|
},
|
|
posts: {
|
|
create: [
|
|
{ title: 'First Post' },
|
|
{ title: 'Second Post' }
|
|
]
|
|
}
|
|
},
|
|
include: {
|
|
profile: true,
|
|
posts: true
|
|
}
|
|
});
|
|
|
|
// Mehrere Datensätze
|
|
const { count } = await prisma.user.createMany({
|
|
data: [
|
|
{ email: 'bob@example.com', name: 'Bob' },
|
|
{ email: 'carol@example.com', name: 'Carol' }
|
|
],
|
|
skipDuplicates: true
|
|
});
|
|
```
|
|
|
|
### Read
|
|
|
|
```typescript
|
|
// Einzelner Datensatz
|
|
const user = await prisma.user.findUnique({
|
|
where: { email: 'alice@example.com' }
|
|
});
|
|
|
|
// Mit Relations
|
|
const userWithPosts = await prisma.user.findUnique({
|
|
where: { id: userId },
|
|
include: {
|
|
posts: {
|
|
where: { published: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 5
|
|
},
|
|
profile: true
|
|
}
|
|
});
|
|
|
|
// Nur bestimmte Felder
|
|
const userBasic = await prisma.user.findUnique({
|
|
where: { id: userId },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true
|
|
}
|
|
});
|
|
|
|
// Liste mit Filter
|
|
const users = await prisma.user.findMany({
|
|
where: {
|
|
role: 'USER',
|
|
email: {
|
|
contains: '@example.com'
|
|
},
|
|
posts: {
|
|
some: {
|
|
published: true
|
|
}
|
|
}
|
|
},
|
|
orderBy: [
|
|
{ role: 'asc' },
|
|
{ name: 'asc' }
|
|
],
|
|
skip: 0,
|
|
take: 10
|
|
});
|
|
```
|
|
|
|
### Update
|
|
|
|
```typescript
|
|
// Einzelner Datensatz
|
|
const updatedUser = await prisma.user.update({
|
|
where: { id: userId },
|
|
data: {
|
|
name: 'Alice Updated',
|
|
// Nested Update
|
|
profile: {
|
|
update: {
|
|
bio: 'Senior Developer from Berlin'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Upsert (Create or Update)
|
|
const user = await prisma.user.upsert({
|
|
where: { email: 'alice@example.com' },
|
|
update: { name: 'Alice' },
|
|
create: {
|
|
email: 'alice@example.com',
|
|
name: 'Alice'
|
|
}
|
|
});
|
|
|
|
// Mehrere aktualisieren
|
|
const { count } = await prisma.user.updateMany({
|
|
where: {
|
|
role: 'USER',
|
|
createdAt: {
|
|
lt: new Date('2024-01-01')
|
|
}
|
|
},
|
|
data: {
|
|
role: 'MODERATOR'
|
|
}
|
|
});
|
|
```
|
|
|
|
### Delete
|
|
|
|
```typescript
|
|
// Einzelner Datensatz
|
|
const deletedUser = await prisma.user.delete({
|
|
where: { id: userId }
|
|
});
|
|
|
|
// Mehrere Datensätze
|
|
const { count } = await prisma.user.deleteMany({
|
|
where: {
|
|
posts: {
|
|
none: {}
|
|
},
|
|
createdAt: {
|
|
lt: new Date('2023-01-01')
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Erweiterte Queries
|
|
|
|
### Aggregations
|
|
|
|
```typescript
|
|
// Count
|
|
const userCount = await prisma.user.count({
|
|
where: { role: 'USER' }
|
|
});
|
|
|
|
// Aggregate
|
|
const stats = await prisma.post.aggregate({
|
|
_count: { id: true },
|
|
_avg: { viewCount: true },
|
|
_sum: { viewCount: true },
|
|
_min: { createdAt: true },
|
|
_max: { createdAt: true }
|
|
});
|
|
|
|
// Group By
|
|
const postsByAuthor = await prisma.post.groupBy({
|
|
by: ['authorId'],
|
|
_count: { id: true },
|
|
_sum: { viewCount: true },
|
|
having: {
|
|
id: {
|
|
_count: {
|
|
gt: 5
|
|
}
|
|
}
|
|
},
|
|
orderBy: {
|
|
_count: {
|
|
id: 'desc'
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
### Raw SQL
|
|
|
|
```typescript
|
|
// Raw Query
|
|
const users = await prisma.$queryRaw<User[]>`
|
|
SELECT * FROM "User"
|
|
WHERE email LIKE ${'%@example.com'}
|
|
ORDER BY "createdAt" DESC
|
|
`;
|
|
|
|
// Raw Execute
|
|
const result = await prisma.$executeRaw`
|
|
UPDATE "User"
|
|
SET "lastLogin" = NOW()
|
|
WHERE id = ${userId}
|
|
`;
|
|
|
|
// Mit TypedSQL (Prisma 7.1+)
|
|
import { sql } from '@prisma/client/sql';
|
|
|
|
const activeUsers = await prisma.$queryRawTyped(
|
|
sql`SELECT * FROM "User" WHERE "isActive" = true`
|
|
);
|
|
```
|
|
|
|
### Transactions
|
|
|
|
```typescript
|
|
// Interactive Transaction
|
|
const result = await prisma.$transaction(async (tx) => {
|
|
// Guthaben abziehen
|
|
const sender = await tx.account.update({
|
|
where: { id: senderId },
|
|
data: { balance: { decrement: amount } }
|
|
});
|
|
|
|
if (sender.balance < 0) {
|
|
throw new Error('Insufficient funds');
|
|
}
|
|
|
|
// Guthaben hinzufügen
|
|
const receiver = await tx.account.update({
|
|
where: { id: receiverId },
|
|
data: { balance: { increment: amount } }
|
|
});
|
|
|
|
return { sender, receiver };
|
|
}, {
|
|
isolationLevel: 'Serializable',
|
|
timeout: 10000
|
|
});
|
|
|
|
// Sequential Operations (Batch)
|
|
const [users, posts] = await prisma.$transaction([
|
|
prisma.user.findMany(),
|
|
prisma.post.findMany({ where: { published: true } })
|
|
]);
|
|
```
|
|
|
|
---
|
|
|
|
## Type-Safety Features
|
|
|
|
### Prisma 7 Typed Improvements
|
|
|
|
```typescript
|
|
// 98% weniger Types bei Schema-Evaluation
|
|
// 45% weniger Types bei Query-Evaluation
|
|
// 70% schnelleres Type-Checking
|
|
|
|
// Full Type Safety
|
|
const user = await prisma.user.findUnique({
|
|
where: { id: userId },
|
|
include: { posts: true }
|
|
});
|
|
|
|
// user ist typisiert als:
|
|
// {
|
|
// id: string;
|
|
// email: string;
|
|
// name: string | null;
|
|
// role: Role;
|
|
// posts: Post[];
|
|
// ...
|
|
// } | null
|
|
|
|
// Conditional Include Typing
|
|
const userMaybeWithPosts = await prisma.user.findUnique({
|
|
where: { id: userId },
|
|
include: includePosts ? { posts: true } : undefined
|
|
});
|
|
// Typ reflektiert die Bedingung
|
|
```
|
|
|
|
### Validierung mit Zod
|
|
|
|
```typescript
|
|
import { z } from 'zod';
|
|
import { Prisma } from '@prisma/client';
|
|
|
|
// Schema aus Prisma Types ableiten
|
|
const CreateUserSchema = z.object({
|
|
email: z.string().email(),
|
|
name: z.string().min(2).optional(),
|
|
role: z.enum(['USER', 'ADMIN', 'MODERATOR']).default('USER')
|
|
}) satisfies z.Schema<Prisma.UserCreateInput>;
|
|
|
|
// Validierung vor DB-Operation
|
|
async function createUser(input: unknown) {
|
|
const data = CreateUserSchema.parse(input);
|
|
return prisma.user.create({ data });
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## SQL Comments (Prisma 7.1)
|
|
|
|
```typescript
|
|
// Observability und Debugging
|
|
const users = await prisma.user.findMany({
|
|
// Diese Informationen erscheinen als SQL-Kommentar
|
|
// für besseres Tracing
|
|
}).$extends({
|
|
query: {
|
|
$allOperations({ operation, args, query }) {
|
|
return query(args);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Generiertes SQL:
|
|
// /* prisma:client,user.findMany,requestId:abc123 */
|
|
// SELECT * FROM "User"
|
|
```
|
|
|
|
---
|
|
|
|
## Serverless & Edge Optimierung
|
|
|
|
```typescript
|
|
// Für Cloudflare Workers, Vercel Edge, etc.
|
|
import { PrismaClient } from '@prisma/client/edge';
|
|
import { withAccelerate } from '@prisma/extension-accelerate';
|
|
|
|
const prisma = new PrismaClient().$extends(withAccelerate());
|
|
|
|
// Connection Pooling via Prisma Accelerate
|
|
const users = await prisma.user.findMany({
|
|
cacheStrategy: {
|
|
ttl: 60, // Cache für 60 Sekunden
|
|
swr: 300 // Stale-While-Revalidate für 5 Minuten
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Migration & Deployment
|
|
|
|
```bash
|
|
# Schema-Änderungen als Migration
|
|
npx prisma migrate dev --name add_user_role
|
|
|
|
# Production Deployment
|
|
npx prisma migrate deploy
|
|
|
|
# Client generieren
|
|
npx prisma generate
|
|
|
|
# Datenbank seeden
|
|
npx prisma db seed
|
|
|
|
# Studio (Daten-Browser)
|
|
npx prisma studio
|
|
```
|
|
|
|
### Seed Script
|
|
|
|
```typescript
|
|
// prisma/seed.ts
|
|
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
// Admin User
|
|
await prisma.user.upsert({
|
|
where: { email: 'admin@example.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'admin@example.com',
|
|
name: 'Admin',
|
|
role: 'ADMIN'
|
|
}
|
|
});
|
|
|
|
// Categories
|
|
const categories = ['Technology', 'Design', 'Business'];
|
|
for (const name of categories) {
|
|
await prisma.category.upsert({
|
|
where: { name },
|
|
update: {},
|
|
create: { name }
|
|
});
|
|
}
|
|
}
|
|
|
|
main()
|
|
.catch(console.error)
|
|
.finally(() => prisma.$disconnect());
|
|
```
|
|
|
|
---
|
|
|
|
## Fazit
|
|
|
|
Prisma 7 bringt:
|
|
|
|
1. **90% kleinere Bundles**: Perfekt für Serverless
|
|
2. **3x schnellere Queries**: Besonders bei großen Datasets
|
|
3. **Bessere Type-Safety**: 70% schnelleres Type-Checking
|
|
4. **SQL Comments**: Verbessertes Debugging
|
|
|
|
Der Rust-zu-TypeScript-Rewrite macht Prisma schneller, kleiner und besser wartbar.
|
|
|
|
---
|
|
|
|
## Bildprompts
|
|
|
|
1. "Database schema transforming into TypeScript types, code generation visualization"
|
|
2. "Performance comparison chart showing bundle size reduction, before/after"
|
|
3. "Prisma logo with speed lines, serverless deployment concept"
|
|
|
|
---
|
|
|
|
## Quellen
|
|
|
|
- [Prisma 7 Release Announcement](https://www.prisma.io/blog/announcing-prisma-orm-7-0-0)
|
|
- [Prisma 7 Upgrade Guide](https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7)
|
|
- [Prisma Performance Benchmarks](https://www.prisma.io/blog/prisma-orm-without-rust-latest-performance-benchmarks)
|
|
- [Prisma Documentation](https://www.prisma.io/docs)
|