Prisma ORM - Next-Generation Database Toolkit
Prisma হলো একটি modern, type-safe ORM যা database operations কে সহজ এবং নিরাপদ করে।
কেন Prisma?
Traditional ORMs এর সমস্যা:
- Type safety নেই
- Complex queries লেখা কঠিন
- Runtime errors
- Poor auto-completion
- Migration management জটিল
Prisma এর সুবিধা:
- ✅ Type-safe: Full TypeScript support
- ✅ Auto-completion: Excellent IntelliSense
- ✅ Intuitive API: Easy to learn
- ✅ Prisma Studio: Visual database browser
- ✅ Migration system: Simple and powerful
- ✅ Multiple databases: PostgreSQL, MySQL, SQLite, MongoDB, SQL Server
- ✅ Performance: Optimized queries
Installation
bash
npm install prisma --save-dev
npm install @prisma/client
# Initialize Prisma
npx prisma initএটি তৈরি করবে:
prisma/schema.prisma- Database schema.env- Environment variables
Schema Definition
prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
tags Tag[]
createdAt DateTime @default(now())
}
model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}Environment Setup
env
# .env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"Generate Prisma Client
bash
npx prisma generateএটি type-safe Prisma Client তৈরি করবে।
Migrations
bash
# Create migration
npx prisma migrate dev --name init
# Apply migrations in production
npx prisma migrate deploy
# Reset database
npx prisma migrate resetBasic CRUD Operations
Setup Prisma Client
typescript
// prisma.ts
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default prisma;Create
typescript
import prisma from "./prisma";
// Create single user
const user = await prisma.user.create({
data: {
email: "john@example.com",
name: "John Doe",
},
});
// Create with relations
const userWithPost = await prisma.user.create({
data: {
email: "jane@example.com",
name: "Jane Doe",
posts: {
create: [
{ title: "My first post", content: "Hello World!" },
{ title: "My second post", content: "Learning Prisma" },
],
},
},
include: {
posts: true,
},
});
// Create many
const users = await prisma.user.createMany({
data: [
{ email: "user1@example.com", name: "User 1" },
{ email: "user2@example.com", name: "User 2" },
],
});Read
typescript
// Find all
const users = await prisma.user.findMany();
// Find with conditions
const publishedPosts = await prisma.post.findMany({
where: { published: true },
});
// Find unique
const user = await prisma.user.findUnique({
where: { email: "john@example.com" },
});
// Find first
const firstUser = await prisma.user.findFirst({
where: { name: { contains: "John" } },
});
// Include relations
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: true,
profile: true,
},
});
// Select specific fields
const userEmails = await prisma.user.findMany({
select: {
email: true,
name: true,
},
});Update
typescript
// Update single
const updatedUser = await prisma.user.update({
where: { id: 1 },
data: { name: "Updated Name" },
});
// Update many
const updateResult = await prisma.post.updateMany({
where: { published: false },
data: { published: true },
});
// Upsert (update or create)
const user = await prisma.user.upsert({
where: { email: "john@example.com" },
update: { name: "John Updated" },
create: { email: "john@example.com", name: "John Doe" },
});Delete
typescript
// Delete single
const deletedUser = await prisma.user.delete({
where: { id: 1 },
});
// Delete many
const deleteResult = await prisma.post.deleteMany({
where: { published: false },
});Advanced Queries
Filtering
typescript
// AND condition
const users = await prisma.user.findMany({
where: {
AND: [
{ email: { contains: "@example.com" } },
{ name: { startsWith: "J" } },
],
},
});
// OR condition
const posts = await prisma.post.findMany({
where: {
OR: [
{ title: { contains: "Prisma" } },
{ content: { contains: "Prisma" } },
],
},
});
// NOT condition
const unpublishedPosts = await prisma.post.findMany({
where: {
NOT: { published: true },
},
});Sorting
typescript
const users = await prisma.user.findMany({
orderBy: {
createdAt: "desc",
},
});
// Multiple sort fields
const posts = await prisma.post.findMany({
orderBy: [{ published: "desc" }, { createdAt: "desc" }],
});Pagination
typescript
// Skip and take
const users = await prisma.user.findMany({
skip: 10,
take: 5,
});
// Cursor-based pagination
const users = await prisma.user.findMany({
take: 10,
cursor: {
id: lastUserId,
},
skip: 1, // Skip the cursor
});Aggregation
typescript
// Count
const userCount = await prisma.user.count();
// Count with conditions
const publishedCount = await prisma.post.count({
where: { published: true },
});
// Aggregate
const result = await prisma.post.aggregate({
_count: true,
_avg: { authorId: true },
_sum: { authorId: true },
_min: { createdAt: true },
_max: { createdAt: true },
});
// Group by
const groupedPosts = await prisma.post.groupBy({
by: ["authorId"],
_count: {
id: true,
},
});Relations
One-to-Many
typescript
// Get user with all posts
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
},
},
});One-to-One
typescript
// Get user with profile
const userWithProfile = await prisma.user.findUnique({
where: { id: 1 },
include: {
profile: true,
},
});Many-to-Many
typescript
// Get post with tags
const postWithTags = await prisma.post.findUnique({
where: { id: 1 },
include: {
tags: true,
},
});
// Create post with tags
const post = await prisma.post.create({
data: {
title: "My Post",
tags: {
connect: [{ id: 1 }, { id: 2 }],
},
},
});Transactions
typescript
// Sequential operations
const result = await prisma.$transaction(async (prisma) => {
const user = await prisma.user.create({
data: { email: "john@example.com", name: "John" },
});
const post = await prisma.post.create({
data: {
title: "First Post",
authorId: user.id,
},
});
return { user, post };
});
// Batch operations
const [deleteUsers, createUser] = await prisma.$transaction([
prisma.user.deleteMany({ where: { name: "Test" } }),
prisma.user.create({ data: { email: "new@example.com", name: "New User" } }),
]);Raw Queries
typescript
// Raw query
const users = await prisma.$queryRaw`SELECT * FROM User WHERE name = ${"John"}`;
// Execute raw SQL
const result =
await prisma.$executeRaw`UPDATE User SET name = 'Updated' WHERE id = ${1}`;Prisma with Express
typescript
import express from "express";
import prisma from "./prisma";
const app = express();
app.use(express.json());
// Get all users
app.get("/users", async (req, res) => {
const users = await prisma.user.findMany();
res.json(users);
});
// Create user
app.post("/users", async (req, res) => {
const user = await prisma.user.create({
data: req.body,
});
res.json(user);
});
// Get user by ID
app.get("/users/:id", async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: Number(req.params.id) },
include: { posts: true },
});
res.json(user);
});
app.listen(3000);Prisma Studio
Visual database browser:
bash
npx prisma studioOpens at http://localhost:5555
Best Practices
- Use transactions for related operations
- Select only needed fields for performance
- Use pagination for large datasets
- Index frequently queried fields
- Use connection pooling in production
- Handle errors properly
- Use migrations for schema changes
TIP
Prisma এর type-safety আপনাকে runtime errors থেকে বাঁচায়। সবসময় Prisma Client regenerate করুন schema change করার পর।