Prisma ORM چیست؟ آموزش کامل Prisma با TypeScript، Node.js و PostgreSQL
در این آموزش Prisma ORM را با TypeScript، Node.js و PostgreSQL راهاندازی میکنیم و Schema، Migration، CRUD، Relation، Transaction، Pagination و ساخت دیتابیس یک برنامه هوش مصنوعی را بهصورت عملی یاد میگیریم.
ساخت یک Backend واقعی فقط به دریافت Request و ارسال Response محدود نمیشود. تقریباً تمام برنامههای کاربردی باید اطلاعات کاربران، تنظیمات، سفارشها، مکالمات، پیامها یا گزارشهای مصرف را در دیتابیس ذخیره کنند.
در پروژههای Node.js و TypeScript میتوان Queryهای SQL را مستقیماً نوشت، اما با بزرگتر شدن برنامه باید مشکلاتی مانند هماهنگ نگه داشتن Typeها با ساختار دیتابیس، مدیریت Migrationها، ارتباط میان جدولها، تراکنشها و Queryهای تکراری را نیز مدیریت کرد.
Prisma ORM ابزاری است که دسترسی به دیتابیس را برای توسعهدهندگان TypeScript ساختاریافتهتر میکند. در Prisma مدلهای دیتابیس در یک Schema تعریف میشوند و سپس یک Client اختصاصی و Type-Safe برای همان مدلها تولید میشود.
در این مقاله Prisma ORM را با PostgreSQL و TypeScript راهاندازی میکنیم، عملیات CRUD و Relationها را میسازیم و در پایان یک ساختار واقعی برای ذخیره مکالمات یک برنامه هوش مصنوعی پیادهسازی میکنیم.
Prisma ORM چیست؟
Prisma ORM یک ابزار دسترسی به دیتابیس برای JavaScript و TypeScript است. توسعهدهنده مدلهای داده را در Prisma Schema تعریف میکند و Prisma براساس همان مدلها یک Client اختصاصی تولید میکند.
این Client امکانات زیر را فراهم میکند:
- Queryهای Type-Safe
- تکمیل خودکار کد در ویرایشگر
- عملیات CRUD
- مدیریت ارتباط میان جدولها
- Transaction
- Migration
- Introspection دیتابیس موجود
- ابزار گرافیکی Prisma Studio
- پشتیبانی از دیتابیسهایی مانند PostgreSQL، MySQL، SQLite، SQL Server، CockroachDB و MongoDB
براساس مستندات رسمی Prisma ORM، Prisma Client از روی Schema پروژه تولید میشود و API آن با مدلها، فیلدها و Relationهای همان پروژه هماهنگ است.
یک Query ساده در Prisma:
const users = await prisma.user.findMany({
where: {
active: true,
},
orderBy: {
createdAt: "desc",
},
});
در این Query، نام مدل user، فیلد active و مقدار createdAt توسط TypeScript بررسی میشوند. اگر فیلدی وجود نداشته باشد یا Type اشتباهی ارسال شود، ویرایشگر و TypeScript خطا را نشان میدهند.
ORM چیست؟
ORM مخفف Object-Relational Mapping است. ORM یک لایه میان کد برنامه و دیتابیس رابطهای ایجاد میکند تا توسعهدهنده بتواند بهجای نوشتن تمام Queryها بهصورت SQL خام، با Objectها و API زبان برنامهنویسی کار کند.
یک Query SQL:
SELECT id, name, email
FROM users
WHERE active = true
ORDER BY created_at DESC;
نمونه مفهومی همان Query در Prisma:
const users = await prisma.user.findMany({
where: {
active: true,
},
select: {
id: true,
name: true,
email: true,
},
orderBy: {
createdAt: "desc",
},
});
استفاده از ORM به معنی بینیازی از SQL نیست. برای طراحی Schema، Index، Relation، Transaction و بهینهسازی Queryها همچنان باید مفاهیم دیتابیس را بدانید.
اجزای اصلی Prisma
Prisma ORM را میتوان به چند بخش اصلی تقسیم کرد.
Prisma Schema
فایلی است که مدلهای داده، Relationها، Enumها، Generator و نوع دیتابیس را تعریف میکند.
مسیر رایج:
prisma/schema.prisma
Prisma Client
Client تولیدشدهای است که برنامه با استفاده از آن Queryهای دیتابیس را اجرا میکند.
const user = await prisma.user.findUnique({
where: {
id: userId,
},
});
Prisma Migrate
ابزار مدیریت تغییرات ساختار دیتابیس است. وقتی مدل جدیدی اضافه یا یک فیلد تغییر میکند، Prisma میتواند Migration متناظر را تولید و اجرا کند.
Prisma Studio
یک رابط گرافیکی برای مشاهده و ویرایش رکوردهای دیتابیس در محیط توسعه است.
npx prisma studio
Prisma CLI
فرمانهایی مانند موارد زیر را ارائه میکند:
npx prisma init
npx prisma generate
npx prisma migrate dev
npx prisma migrate deploy
npx prisma db pull
npx prisma db push
npx prisma studio
Prisma چه تفاوتی با SQL خام دارد؟
| معیار | Prisma ORM | SQL خام |
|---|---|---|
| Type Safety | قوی در TypeScript | نیازمند ابزار یا Type دستی |
| Autocomplete | دارد | معمولاً محدود |
| سرعت توسعه CRUD | بالا | نیازمند Query بیشتر |
| کنترل کامل Query | محدودتر | بسیار بالا |
| Migration | ابزار داخلی | نیازمند ابزار جداگانه یا Script |
| Queryهای پیچیده تحلیلی | گاهی دشوارتر | انعطافپذیرتر |
| وابستگی به ORM | دارد | ندارد |
| نیاز به دانش SQL | همچنان لازم است | ضروری است |
در بسیاری از پروژهها بهترین راهکار استفاده ترکیبی است: بیشتر عملیات عادی با Prisma انجام میشوند و برای Queryهای تحلیلی یا خاص از SQL کنترلشده استفاده میشود.
Prisma برای چه پروژههایی مناسب است؟
Prisma انتخاب مناسبی برای بسیاری از پروژههای زیر است:
- Backendهای TypeScript و Node.js
- REST API و GraphQL API
- پروژههای Express و NestJS
- برنامههای Next.js
- پنلهای مدیریتی
- نرمافزارهای SaaS
- فروشگاههای اینترنتی
- سیستمهای مدیریت محتوا
- چتباتهای هوش مصنوعی
- AI Agentها
- سیستمهای RAG
- ذخیره تاریخچه مکالمات
- ثبت مصرف API و گزارشهای مالی
در پروژههایی که Queryهای تحلیلی بسیار پیچیده، پردازش حجیم یا نیازهای کاملاً اختصاصی دیتابیس دارند، ممکن است ترکیب Prisma با SQL خام یا استفاده از ابزار دیگری مناسبتر باشد.
نسخه Prisma و تغییرات مهم Prisma 7
در زمان نگارش این مقاله، Prisma 7 نسخه پایدار عمومی Prisma ORM است و Prisma Next نیز بهعنوان مسیر نسخه اصلی بعدی در حال توسعه است. برای پروژه Production بهتر است از نسخه پایدار استفاده کنید، مگر اینکه دلیل مشخصی برای آزمایش نسخه Early Access داشته باشید.
در Prisma 7 چند تغییر مهم وجود دارد:
- Generator جدید
prisma-clientاستفاده میشود. - مسیر خروجی Prisma Client باید مشخص شود.
- اتصال دیتابیس با Driver Adapter انجام میشود.
- URL دیتابیس معمولاً در
prisma.config.tsتنظیم میشود. - اجرای
prisma migrate devدیگر لزوماً Prisma Client را تولید نمیکند؛ بنابراینprisma generateرا جداگانه اجرا کنید.
جزئیات تغییرات در راهنمای رسمی ارتقا به Prisma 7 قرار دارد.
نمونههای این مقاله بر پایه Prisma ORM 7 نوشته شدهاند.
پیشنیازهای آموزش
برای اجرای پروژه به موارد زیر نیاز دارید:
- Node.js نسخه LTS یا جدیدتر
- npm یا pnpm
- TypeScript
- یک دیتابیس PostgreSQL
- آشنایی مقدماتی با Node.js
- یک ویرایشگر مانند VS Code
نسخهها را بررسی کنید:
node --version
npm --version
برای PostgreSQL میتوانید از نصب محلی، Docker یا یک سرویس مدیریتشده استفاده کنید.
ساخت پروژه TypeScript
پوشه پروژه را بسازید:
mkdir prisma-ai-api
cd prisma-ai-api
پروژه npm را راهاندازی کنید:
npm init -y
TypeScript و ابزار اجرای آن را نصب کنید:
npm install --save-dev typescript tsx @types/node
فایل تنظیمات TypeScript را بسازید:
npx tsc --init
نمونه tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": [
"src",
"generated",
"prisma.config.ts"
]
}
در package.json حالت ES Module را فعال کنید:
{
"type": "module"
}
نصب Prisma برای PostgreSQL
بستههای مورد نیاز را نصب کنید:
npm install @prisma/client @prisma/adapter-pg pg dotenv
npm install --save-dev prisma @types/pg
Prisma را راهاندازی کنید:
npx prisma init --datasource-provider postgresql
این فرمان معمولاً فایلها و پوشههای اصلی Prisma را میسازد:
prisma/
schema.prisma
prisma.config.ts
.env
راهنمای رسمی راهاندازی PostgreSQL در Quickstart رسمی Prisma و PostgreSQL موجود است.
ساخت دیتابیس PostgreSQL با Docker
اگر PostgreSQL محلی ندارید، میتوانید برای محیط توسعه از Docker استفاده کنید.
فایل compose.yaml:
services:
postgres:
image: postgres:17
container_name: prisma_ai_postgres
restart: unless-stopped
environment:
POSTGRES_USER: app_user
POSTGRES_PASSWORD: change_this_password
POSTGRES_DB: ai_app
ports:
- "5432:5432"
volumes:
- prisma_postgres_data:/var/lib/postgresql/data
volumes:
prisma_postgres_data:
دیتابیس را اجرا کنید:
docker compose up -d
وضعیت Container:
docker compose ps
برای محیط Production نباید از رمز نمونه مقاله استفاده کنید. Secretها باید خارج از مخزن و از طریق محیط استقرار مدیریت شوند.
تنظیم DATABASE_URL
در فایل .env:
DATABASE_URL="postgresql://app_user:change_this_password@localhost:5432/ai_app?schema=public"
ساختار کلی Connection String:
postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA
فایل .env را به Git اضافه نکنید:
.env
.env.*
!.env.example
فایل .env.example:
DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/DATABASE?schema=public"
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
اگر نام کاربری یا رمز عبور شامل کاراکترهای خاص است، آنها را در Connection String بهدرستی URL Encode کنید.
تنظیم prisma.config.ts
در Prisma 7 میتوان تنظیمات مسیر Schema، Migration و URL دیتابیس را در prisma.config.ts قرار داد:
import "dotenv/config";
import {
defineConfig,
env,
} from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: env("DATABASE_URL"),
},
});
این فایل توسط Prisma CLI استفاده میشود.
ساخت Prisma Schema
فایل prisma/schema.prisma را باز کنید:
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
اکنون مدلهای پروژه را اضافه میکنیم.
ساخت مدل User
model User {
id String @id @default(uuid())
email String @unique
name String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([createdAt])
}
مفهوم هر بخش:
@id: کلید اصلی@default(uuid()): تولید UUID بهصورت پیشفرض@unique: جلوگیری از ثبت مقدار تکراریString?: فیلد اختیاری یا Nullable@default(true): مقدار پیشفرض@default(now()): زمان ایجاد@updatedAt: بهروزرسانی خودکار زمان ویرایش@@index: تعریف Index
ساخت اولین Migration
پس از تعریف مدل:
npx prisma migrate dev --name init
این فرمان در محیط توسعه:
- اختلاف Schema و دیتابیس را بررسی میکند.
- فایل Migration میسازد.
- Migration را روی دیتابیس Development اجرا میکند.
در Prisma 7، Prisma Client را جداگانه تولید کنید:
npx prisma generate
ساختار پروژه اکنون تقریباً چنین است:
prisma-ai-api/
generated/
prisma/
prisma/
migrations/
schema.prisma
src/
.env
prisma.config.ts
package.json
tsconfig.json
ساخت Prisma Client
فایل src/lib/prisma.ts:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../generated/prisma/client";
const connectionString =
process.env.DATABASE_URL;
if (!connectionString) {
throw new Error(
"DATABASE_URL is not configured",
);
}
const adapter = new PrismaPg({
connectionString,
});
export const prisma = new PrismaClient({
adapter,
});
در Prisma 7، Driver Adapter اتصال Prisma Client به Driver دیتابیس را برقرار میکند. برای PostgreSQL از @prisma/adapter-pg استفاده کردهایم.
آزمایش اتصال به دیتابیس
فایل src/index.ts:
import { prisma } from "./lib/prisma.js";
async function main() {
const userCount =
await prisma.user.count();
console.log({
connected: true,
userCount,
});
}
main()
.catch((error) => {
console.error(error);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});
اجرا:
npx tsx src/index.ts
خروجی نمونه:
{ connected: true, userCount: 0 }
در Scriptهای کوتاه میتوان در finally اتصال را قطع کرد. در یک Server طولانیمدت نباید بعد از هر Request از $disconnect() استفاده کنید؛ زیرا Server باید Connectionها را برای درخواستهای بعدی نگه دارد.
عملیات CRUD با Prisma
CRUD شامل Create، Read، Update و Delete است.
ایجاد رکورد با create
const user = await prisma.user.create({
data: {
email: "sara@example.com",
name: "Sara Ahmadi",
},
});
console.log(user);
خروجی شامل فیلدهای تولیدشده مانند id و createdAt خواهد بود.
دریافت تمام کاربران با findMany
const users = await prisma.user.findMany({
orderBy: {
createdAt: "desc",
},
});
console.log(users);
دریافت کاربر با findUnique
findUnique برای فیلدهای Unique مانند id و email استفاده میشود:
const user = await prisma.user.findUnique({
where: {
email: "sara@example.com",
},
});
اگر رکورد وجود نداشته باشد، خروجی null است.
if (!user) {
console.log("User not found");
}
دریافت اولین رکورد مطابق شرط
const user = await prisma.user.findFirst({
where: {
active: true,
name: {
contains: "Sara",
mode: "insensitive",
},
},
});
بهروزرسانی با update
const updatedUser =
await prisma.user.update({
where: {
email: "sara@example.com",
},
data: {
name: "Sara A.",
active: true,
},
});
اگر رکورد مورد نظر وجود نداشته باشد، update خطا ایجاد میکند.
ایجاد یا بهروزرسانی با upsert
const user = await prisma.user.upsert({
where: {
email: "sara@example.com",
},
update: {
active: true,
},
create: {
email: "sara@example.com",
name: "Sara Ahmadi",
},
});
upsert برای مواردی مناسب است که یک رکورد باید در هر صورت پس از عملیات وجود داشته باشد.
حذف رکورد با delete
await prisma.user.delete({
where: {
email: "sara@example.com",
},
});
حذف داده باید آگاهانه انجام شود. در سیستمهای واقعی گاهی Soft Delete، Archive یا نگهداری تاریخچه به حذف فیزیکی ترجیح داده میشود.
حذف چند رکورد با deleteMany
const result =
await prisma.user.deleteMany({
where: {
active: false,
},
});
console.log(result.count);
پیش از اجرای deleteMany در محیط واقعی، شرط where را با دقت بررسی و ابتدا Query مشابه را با findMany آزمایش کنید.
جزئیات تمام عملیات در مستندات رسمی CRUD در Prisma موجود است.
انتخاب فیلدها با select
اگر تمام فیلدهای مدل را نیاز ندارید، از select استفاده کنید:
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
},
});
خروجی فقط شامل فیلدهای انتخابشده است.
این روش مزایای مهمی دارد:
- انتقال داده کمتر
- خروجی مشخصتر
- جلوگیری از بازگرداندن تصادفی فیلدهای غیرضروری
- Type دقیقتر در TypeScript
در API عمومی بهتر است Response را آگاهانه بسازید و Object کامل دیتابیس را مستقیماً برنگردانید.
فیلتر کردن دادهها
Prisma امکانات متنوعی برای فیلتر دارد:
const users = await prisma.user.findMany({
where: {
active: true,
email: {
endsWith: "@example.com",
mode: "insensitive",
},
createdAt: {
gte: new Date("2026-01-01"),
},
},
});
ترکیب شرطها با OR:
const users = await prisma.user.findMany({
where: {
OR: [
{
name: {
contains: "Sara",
mode: "insensitive",
},
},
{
email: {
contains: "sara",
mode: "insensitive",
},
},
],
},
});
ترکیب با AND:
const users = await prisma.user.findMany({
where: {
AND: [
{
active: true,
},
{
createdAt: {
gte: new Date("2026-01-01"),
},
},
],
},
});
مرتبسازی دادهها
const users = await prisma.user.findMany({
orderBy: [
{
active: "desc",
},
{
createdAt: "desc",
},
],
});
صفحهبندی با Pagination
صفحهبندی با skip و take
const page = 2;
const limit = 20;
const users = await prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: {
createdAt: "desc",
},
});
برای دریافت تعداد کل:
const [items, total] =
await prisma.$transaction([
prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: {
createdAt: "desc",
},
}),
prisma.user.count(),
]);
Response:
const response = {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
items,
};
این روش برای تعداد رکوردهای معمولی مناسب است، اما skipهای بسیار بزرگ ممکن است کارایی کمتری داشته باشند.
Cursor Pagination
برای لیستهای بزرگ یا Feedها میتوان از Cursor استفاده کرد:
const users = await prisma.user.findMany({
take: 20,
cursor: lastUserId
? {
id: lastUserId,
}
: undefined,
skip: lastUserId ? 1 : 0,
orderBy: {
id: "asc",
},
});
برای Cursor Pagination باید ترتیب پایدار و فیلد مناسب انتخاب شود.
تعریف ارتباط میان مدلها
اکنون میخواهیم هر کاربر چند مکالمه داشته باشد و هر مکالمه شامل چند پیام باشد.
Schema کاملتر:
enum MessageRole {
system
user
assistant
tool
}
model User {
id String @id @default(uuid())
email String @unique
name String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
conversations Conversation[]
@@index([createdAt])
}
model Conversation {
id String @id @default(uuid())
title String?
modelId String
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(
fields: [userId],
references: [id],
onDelete: Cascade
)
messages Message[]
@@index([userId, updatedAt])
}
model Message {
id String @id @default(uuid())
role MessageRole
content String
inputTokens Int?
outputTokens Int?
conversationId String
createdAt DateTime @default(now())
conversation Conversation @relation(
fields: [conversationId],
references: [id],
onDelete: Cascade
)
@@index([conversationId, createdAt])
}
پس از تغییر Schema:
npx prisma migrate dev --name add_conversations
npx prisma generate
مفهوم Relation در Prisma
در مدل Conversation:
userId String
کلید خارجی را نگهداری میکند.
این بخش ارتباط را تعریف میکند:
user User @relation(
fields: [userId],
references: [id],
onDelete: Cascade
)
یعنی:
- هر Conversation به یک User متعلق است.
- مقدار
userIdبهUser.idاشاره میکند. - با حذف User، Conversationهای مرتبط نیز طبق این طراحی حذف میشوند.
انتخاب onDelete: Cascade باید با نیاز محصول، نگهداری سوابق و سیاست داده هماهنگ باشد. در برخی سیستمها Restrict، SetNull یا Soft Delete انتخاب مناسبتری است.
ایجاد Relation با connect
ابتدا کاربر:
const user = await prisma.user.create({
data: {
email: "user@example.com",
name: "کاربر نمونه",
},
});
سپس مکالمه:
const conversation =
await prisma.conversation.create({
data: {
title: "گفتوگوی آزمایشی",
modelId: "YOUR_MODEL_ID",
user: {
connect: {
id: user.id,
},
},
},
});
Nested Write
میتوان کاربر و Conversation را در یک عملیات ساخت:
const user = await prisma.user.create({
data: {
email: "sara@example.com",
name: "Sara",
conversations: {
create: {
title: "اولین گفتوگو",
modelId: "YOUR_MODEL_ID",
},
},
},
include: {
conversations: true,
},
});
Nested Writeهای Prisma به شکل Transactional اجرا میشوند؛ یعنی اگر بخشی از عملیات شکست بخورد، تغییرات مرتبط نیز برگشت داده میشوند.
خواندن Relationها با include
const conversation =
await prisma.conversation.findUnique({
where: {
id: conversationId,
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
messages: {
orderBy: {
createdAt: "asc",
},
},
},
});
include مدلهای مرتبط را به خروجی اضافه میکند.
اگر فقط بعضی فیلدها لازماند، از select استفاده کنید:
const conversations =
await prisma.conversation.findMany({
select: {
id: true,
title: true,
modelId: true,
updatedAt: true,
_count: {
select: {
messages: true,
},
},
},
});
راهنمای کامل در مستندات Relation Queries در Prisma قرار دارد.
فیلتر کردن براساس Relation
تمام کاربرانی که حداقل یک Conversation دارند:
const users = await prisma.user.findMany({
where: {
conversations: {
some: {},
},
},
});
تمام مکالمات یک کاربر:
const conversations =
await prisma.conversation.findMany({
where: {
userId,
},
orderBy: {
updatedAt: "desc",
},
});
مکالماتی که دارای پیام Assistant هستند:
const conversations =
await prisma.conversation.findMany({
where: {
messages: {
some: {
role: "assistant",
},
},
},
});
Transaction در Prisma
Transaction مجموعهای از عملیات است که باید یا همگی موفق شوند یا هیچکدام اعمال نشوند.
فرض کنید میخواهیم هم پیام کاربر و هم پیام Assistant را ذخیره کنیم:
const result = await prisma.$transaction(
async (tx) => {
const userMessage =
await tx.message.create({
data: {
conversationId,
role: "user",
content: userContent,
},
});
const assistantMessage =
await tx.message.create({
data: {
conversationId,
role: "assistant",
content: assistantContent,
inputTokens,
outputTokens,
},
});
await tx.conversation.update({
where: {
id: conversationId,
},
data: {
updatedAt: new Date(),
},
});
return {
userMessage,
assistantMessage,
};
},
);
اگر ساخت پیام Assistant شکست بخورد، پیام User نیز در دیتابیس باقی نمیماند.
Prisma از Transactionهای آرایهای نیز پشتیبانی میکند:
const [conversation, messageCount] =
await prisma.$transaction([
prisma.conversation.findUnique({
where: {
id: conversationId,
},
}),
prisma.message.count({
where: {
conversationId,
},
}),
]);
مستندات رسمی انواع Transaction را در راهنمای Transactions and Batch Queries توضیح داده است.
آیا فراخوانی API هوش مصنوعی را داخل Transaction قرار دهیم؟
معمولاً بهتر است Transaction دیتابیس را هنگام انتظار برای یک درخواست شبکه طولانی باز نگه ندارید.
این الگو مناسب نیست:
await prisma.$transaction(async (tx) => {
await tx.message.create({
data: userMessage,
});
const aiResponse =
await callExternalAiApi();
await tx.message.create({
data: aiResponse,
});
});
درخواست مدل هوش مصنوعی ممکن است چند ثانیه طول بکشد. باز ماندن Transaction در این مدت میتواند Connection دیتابیس را اشغال کند و احتمال Timeout یا رقابت همزمان را افزایش دهد.
الگوی بهتر:
- پیام User را ذخیره کنید.
- درخواست API هوش مصنوعی را خارج از Transaction اجرا کنید.
- پاسخ را اعتبارسنجی کنید.
- پیام Assistant و اطلاعات مصرف را در یک Transaction کوتاه ذخیره کنید.
- در صورت شکست API، وضعیت پیام یا درخواست را به
failedتغییر دهید.
برای سیستم قابلاعتمادتر میتوان مدل AiRequest یا GenerationJob با وضعیتهای زیر ساخت:
enum GenerationStatus {
pending
processing
completed
failed
}
ساخت سرویس مکالمه هوش مصنوعی
فایل src/services/chat.service.ts:
import { prisma } from "../lib/prisma.js";
type CreateChatInput = {
userId: string;
conversationId: string;
content: string;
};
export async function createChatMessage(
input: CreateChatInput,
) {
const conversation =
await prisma.conversation.findFirst({
where: {
id: input.conversationId,
userId: input.userId,
},
select: {
id: true,
modelId: true,
},
});
if (!conversation) {
throw new Error(
"Conversation not found",
);
}
const userMessage =
await prisma.message.create({
data: {
conversationId: conversation.id,
role: "user",
content: input.content,
},
});
const history =
await prisma.message.findMany({
where: {
conversationId: conversation.id,
},
orderBy: {
createdAt: "asc",
},
take: 30,
select: {
role: true,
content: true,
},
});
return {
conversation,
userMessage,
history,
};
}
در Query اول، userId نیز بررسی شده است تا صرف داشتن شناسه Conversation برای دسترسی کافی نباشد. این نوع شرط باید در تمام Queryهای چندکاربره براساس مدل مجوز پروژه رعایت شود.
اتصال سرویس به API درواره
فایل src/services/ai.service.ts:
type ChatMessage = {
role: string;
content: string;
};
type CreateCompletionInput = {
modelId: string;
messages: ChatMessage[];
};
export async function createCompletion(
input: CreateCompletionInput,
) {
const apiKey =
process.env.DARVAREH_API_KEY;
if (!apiKey) {
throw new Error(
"DARVAREH_API_KEY is missing",
);
}
const response = await fetch(
"https://api.darvareh.ir/v1/chat/completions",
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: input.modelId,
messages: input.messages,
}),
},
);
if (!response.ok) {
throw new Error(
`AI API request failed: ${response.status}`,
);
}
return response.json();
}
کلید API باید فقط در Backend و متغیر محیطی نگهداری شود. قرار دادن کلید در کد React، JavaScript مرورگر یا مخزن عمومی باعث قابلاستخراج شدن آن میشود.
برای دریافت API Key میتوانید در درواره ثبتنام کنید. شناسه مدلها و قیمتهای بهروز در صفحه مدلهای درواره قرار دارند.
ذخیره پاسخ مدل هوش مصنوعی
import { prisma } from "../lib/prisma.js";
import { createCompletion } from "./ai.service.js";
export async function processChat(
userId: string,
conversationId: string,
content: string,
) {
const conversation =
await prisma.conversation.findFirst({
where: {
id: conversationId,
userId,
},
select: {
id: true,
modelId: true,
},
});
if (!conversation) {
throw new Error(
"Conversation not found",
);
}
await prisma.message.create({
data: {
conversationId,
role: "user",
content,
},
});
const history =
await prisma.message.findMany({
where: {
conversationId,
},
orderBy: {
createdAt: "asc",
},
take: 30,
select: {
role: true,
content: true,
},
});
const completion =
await createCompletion({
modelId: conversation.modelId,
messages: history,
});
const assistantContent =
completion.choices?.[0]?.message
?.content;
if (
typeof assistantContent !== "string"
) {
throw new Error(
"Invalid AI response",
);
}
const assistantMessage =
await prisma.$transaction(
async (tx) => {
const message =
await tx.message.create({
data: {
conversationId,
role: "assistant",
content: assistantContent,
},
});
await tx.conversation.update({
where: {
id: conversationId,
},
data: {
updatedAt: new Date(),
},
});
return message;
},
);
return assistantMessage;
}
در پروژه واقعی بهتر است پاسخ API با Schema Runtime مانند Zod اعتبارسنجی شود و وضعیت Request، خطا، تعداد Tokenها و شناسه درخواست نیز ثبت شوند.
جلوگیری از ساخت چند Prisma Client
در یک Backend عادی معمولاً باید یک Prisma Client مشترک برای فرایند برنامه داشته باشید.
فایل Singleton:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../generated/prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma?: PrismaClient;
};
const adapter = new PrismaPg({
connectionString:
process.env.DATABASE_URL!,
});
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
adapter,
});
if (
process.env.NODE_ENV !== "production"
) {
globalForPrisma.prisma = prisma;
}
این الگو در محیط Development و ابزارهایی که Hot Reload دارند، از ساخته شدن Clientهای متعدد جلوگیری میکند.
برای Runtimeهای Serverless باید Connection Pooling، تعداد Instanceها و محدودیت Connection دیتابیس را نیز بررسی کنید. راهنمای رسمی Prisma درباره محیطهای Serverless و فرایندهای طولانی در Database Connections منتشر شده است.
Prisma Studio
برای مشاهده دادهها:
npx prisma studio
مرورگر باز میشود و میتوانید:
- جدولها را ببینید.
- رکوردها را فیلتر کنید.
- داده Development را ویرایش کنید.
- Relationها را مشاهده کنید.
- رکورد آزمایشی بسازید.
Prisma Studio یک ابزار مدیریتی عمومی برای کاربران نهایی نیست. آن را بدون کنترل دسترسی روی اینترنت عمومی منتشر نکنید و دسترسی Production را محدود نگه دارید.
db push چه تفاوتی با migrate dev دارد؟
prisma db push
npx prisma db push
ساختار Schema را بدون تولید تاریخچه Migration روی دیتابیس اعمال میکند.
مناسب برای:
- Prototype
- آزمایش سریع
- پروژه موقت
- محیط Development اولیه
prisma migrate dev
npx prisma migrate dev --name add_messages
Migration ایجاد میکند و آن را در Development اعمال میکند.
مناسب برای:
- پروژه واقعی
- کار تیمی
- نگهداری تاریخچه تغییرات
- استقرار در چند محیط
برای پروژه Production بهتر است Migrationها داخل Git ثبت شوند و تغییرات Schema به شکل قابلردیابی مدیریت شوند.
اجرای Migration در Production
در محیط Production از این فرمان استفاده کنید:
npx prisma migrate deploy
این فرمان Migrationهای موجود و اجرانشده را اعمال میکند و Migration جدید نمیسازد.
فرایند پیشنهادی:
- Schema در Development تغییر میکند.
migrate devفایل Migration را میسازد.- فایل Migration بررسی و وارد Git میشود.
- تستها اجرا میشوند.
- در فرایند Deployment از
migrate deployاستفاده میشود.
در Production نباید migrate dev اجرا شود؛ زیرا این فرمان برای Workflow توسعه طراحی شده است.
تولید Prisma Client در Build
اسکریپتهای package.json:
{
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "prisma generate && tsc",
"start": "node dist/server.js",
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy",
"db:studio": "prisma studio"
}
}
در محیط CI یا Build مطمئن شوید prisma generate اجرا میشود.
پس از هر تغییر Schema:
npx prisma generate
اگر Client دوباره تولید نشود، Typeهای برنامه با Schema جدید هماهنگ نخواهند بود.
Introspection دیتابیس موجود
اگر از قبل دیتابیس دارید، لازم نیست تمام مدلها را دستی تعریف کنید.
ابتدا DATABASE_URL را تنظیم کنید و سپس اجرا کنید:
npx prisma db pull
این فرمان ساختار دیتابیس را بررسی و مدلهای Prisma را ایجاد یا بهروزرسانی میکند.
سپس:
npx prisma generate
پس از Introspection موارد زیر را بررسی کنید:
- نام مدلها و فیلدها
- کلیدهای اصلی
- Relationها
- نوع ستونها
- Indexها
- Viewها و قابلیتهای خاص دیتابیس
- نامگذاری با
@mapو@@map
فایل تولیدشده را بدون بازبینی وارد Production نکنید.
استفاده از @map و @@map
میتوان نام مدل در کد را از نام جدول دیتابیس جدا کرد.
model UserProfile {
id String @id @default(uuid())
firstName String @map("first_name")
lastName String @map("last_name")
@@map("user_profiles")
}
در TypeScript:
await prisma.userProfile.findMany({
select: {
firstName: true,
lastName: true,
},
});
در دیتابیس:
user_profiles
first_name
last_name
این قابلیت هنگام اتصال Prisma به دیتابیس قدیمی یا دارای نامگذاری Snake Case بسیار مفید است.
تعریف Index در Prisma
Index روی فیلدهایی قرار میگیرد که در فیلتر، مرتبسازی، Join یا جستوجو زیاد استفاده میشوند.
model Message {
id String @id @default(uuid())
conversationId String
role MessageRole
content String
createdAt DateTime @default(now())
conversation Conversation @relation(
fields: [conversationId],
references: [id],
onDelete: Cascade
)
@@index([conversationId, createdAt])
}
این Index برای Query زیر مفید است:
await prisma.message.findMany({
where: {
conversationId,
},
orderBy: {
createdAt: "asc",
},
});
افزودن Index به تمام فیلدها روش مناسبی نیست. هر Index فضای ذخیرهسازی مصرف میکند و هزینه عملیات Write را افزایش میدهد. Index باید براساس Queryهای واقعی و Execution Plan انتخاب شود.
Unique Constraint ترکیبی
فرض کنید هر کاربر فقط یک تنظیم برای هر کلید داشته باشد:
model UserSetting {
id String @id @default(uuid())
userId String
key String
value String
@@unique([userId, key])
}
اکنون میتوان با کلید ترکیبی Query زد:
const setting =
await prisma.userSetting.findUnique({
where: {
userId_key: {
userId,
key: "language",
},
},
});
Aggregate و Group By
میانگین Token خروجی:
const result =
await prisma.message.aggregate({
where: {
role: "assistant",
outputTokens: {
not: null,
},
},
_avg: {
outputTokens: true,
},
_sum: {
outputTokens: true,
},
_count: {
id: true,
},
});
گروهبندی براساس Role:
const messages =
await prisma.message.groupBy({
by: ["role"],
_count: {
id: true,
},
_sum: {
inputTokens: true,
outputTokens: true,
},
});
برای گزارشهای تحلیلی پیچیده ممکن است SQL خام، View یا سیستم تحلیلی جداگانه مناسبتر باشد.
استفاده از SQL خام
Prisma امکان اجرای SQL خام را نیز فراهم میکند:
const result =
await prisma.$queryRaw`
SELECT role, COUNT(*)::int AS count
FROM "Message"
GROUP BY role
`;
Template Tag مربوط به Prisma پارامترها را مدیریت میکند. از ساخت Query با String Concatenation خودداری کنید:
const query =
`SELECT * FROM users WHERE email = '${email}'`;
این الگو میتواند Query را آسیبپذیر و مدیریت آن را دشوار کند.
SQL خام را فقط زمانی استفاده کنید که Query Builder معمول Prisma نیاز را پوشش نمیدهد و ورودیها به شکل پارامتری مدیریت میشوند.
Seed کردن دیتابیس
فایل prisma/seed.ts:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../generated/prisma/client";
const adapter = new PrismaPg({
connectionString:
process.env.DATABASE_URL!,
});
const prisma = new PrismaClient({
adapter,
});
async function main() {
const user = await prisma.user.upsert({
where: {
email: "demo@example.com",
},
update: {},
create: {
email: "demo@example.com",
name: "Demo User",
conversations: {
create: {
title: "مکالمه آزمایشی",
modelId: "YOUR_MODEL_ID",
},
},
},
});
console.log(user);
}
main()
.catch((error) => {
console.error(error);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});
اسکریپت:
{
"scripts": {
"db:seed": "tsx prisma/seed.ts"
}
}
اجرا:
npm run db:seed
استفاده از upsert باعث میشود Seed تا حد ممکن تکرارپذیر باشد.
مدیریت خطاهای Prisma
تمام خطاهای دیتابیس نباید با یک پیام عمومی و Status Code یکسان پاسخ داده شوند.
نمونه مدیریت خطای Unique Constraint:
import {
Prisma,
} from "../../generated/prisma/client";
try {
const user = await prisma.user.create({
data: {
email,
name,
},
});
return user;
} catch (error) {
if (
error instanceof
Prisma.PrismaClientKnownRequestError &&
error.code === "P2002"
) {
throw new Error(
"EMAIL_ALREADY_EXISTS",
);
}
throw error;
}
در لایه HTTP میتوان این خطا را به Response مناسب تبدیل کرد:
{
"error": "EMAIL_ALREADY_EXISTS",
"message": "کاربری با این ایمیل وجود دارد."
}
Stack Trace، Connection String، SQL داخلی و جزئیات دیتابیس نباید مستقیماً به کاربر نمایش داده شوند.
اعتبارسنجی ورودی پیش از Prisma
Type-Safe بودن Prisma به معنی معتبر بودن Request کاربر نیست. ورودی API باید پیش از رسیدن به Query اعتبارسنجی شود.
نمونه با Zod:
import { z } from "zod";
const CreateConversationSchema = z.object({
title: z
.string()
.trim()
.min(1)
.max(150)
.optional(),
modelId: z.string().min(1),
});
سپس:
const result =
CreateConversationSchema.safeParse(
req.body,
);
if (!result.success) {
return res.status(400).json({
error: "VALIDATION_ERROR",
});
}
const conversation =
await prisma.conversation.create({
data: {
...result.data,
userId: req.user.id,
},
});
Prisma نوع Query دیتابیس را بررسی میکند و Zod داده Runtime را اعتبارسنجی میکند. این دو ابزار مکمل یکدیگرند.
بهینهسازی Queryهای Prisma
فقط فیلدهای مورد نیاز را انتخاب کنید
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
},
});
از دریافت Relationهای غیرضروری خودداری کنید
این Query ممکن است حجم زیادی داده برگرداند:
await prisma.user.findMany({
include: {
conversations: {
include: {
messages: true,
},
},
},
});
بهتر است Relationهای حجیم را صفحهبندی یا جداگانه دریافت کنید.
Queryهای داخل Loop را بررسی کنید
الگوی N+1:
for (const user of users) {
const conversations =
await prisma.conversation.findMany({
where: {
userId: user.id,
},
});
}
بهتر:
const users = await prisma.user.findMany({
include: {
conversations: {
take: 5,
orderBy: {
updatedAt: "desc",
},
},
},
});
Index متناسب بسازید
فیلدهای پرتکرار در where، orderBy و Relationها را بررسی کنید.
Queryها را اندازهگیری کنید
کند بودن برنامه را فقط به ORM نسبت ندهید. Execution Plan، حجم داده، Index، تعداد Connection و Queryهای N+1 را بررسی کنید.
Logging در Prisma Client
برای محیط Development میتوان Log را فعال کرد:
export const prisma =
new PrismaClient({
adapter,
log: [
"query",
"info",
"warn",
"error",
],
});
در Production ثبت تمام Queryها میتواند حجم Log را زیاد کند و اطلاعات حساس را در معرض ثبت ناخواسته قرار دهد. سطح Log باید براساس نیاز عملیاتی تنظیم شود.
Connection Management
در برنامههای طولانیمدت مانند Express:
- یک Prisma Client مشترک بسازید.
- در هر Request Client جدید ایجاد نکنید.
- بعد از هر Query از
$disconnect()استفاده نکنید. - هنگام خاموش شدن برنامه اتصال را مدیریت کنید.
async function shutdown() {
await prisma.$disconnect();
process.exit(0);
}
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
در محیطهای Serverless:
- تعداد Instanceهای همزمان را در نظر بگیرید.
- از Connection Pooling متناسب استفاده کنید.
- محدودیت Connection دیتابیس را بررسی کنید.
- Client را تا حد امکان خارج Handler بسازید.
- رفتار Runtime و Platform را با تست Load اندازهگیری کنید.
استفاده از Prisma در Next.js
در Next.js به دلیل Hot Reload محیط Development، استفاده از Singleton اهمیت بیشتری دارد:
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
adapter,
});
if (
process.env.NODE_ENV !== "production"
) {
globalForPrisma.prisma = prisma;
}
همچنین Prisma Client و Connection String فقط باید در Server Component، Route Handler، Server Action یا Backend استفاده شوند. آنها را وارد Client Component نکنید.
راهنمای رسمی Prisma برای Next.js در مستندات Prisma و Next.js در دسترس است.
تست Prisma
برای تستهای Integration بهتر است یک دیتابیس مجزا داشته باشید:
DATABASE_URL="postgresql://test_user:test_password@localhost:5433/ai_app_test?schema=public"
پیش از تست:
npx prisma migrate deploy
اصول مهم:
- تستها نباید به دیتابیس Production متصل شوند.
- داده تست باید قابل پاکسازی یا بازسازی باشد.
- Migrationها باید در CI بررسی شوند.
- برای عملیات Transactional تست شکست میانی بنویسید.
- Unique Constraint و Relation Constraint را آزمایش کنید.
- Queryهای مجوز دسترسی چندکاربره را تست کنید.
استفاده از Mock برای Unit Test ممکن است مناسب باشد، اما Queryها و Constraintهای واقعی دیتابیس فقط در Integration Test بهدرستی بررسی میشوند.
Prisma Migrate و تغییرات حساس Schema
بعضی تغییرات ساده و بعضی پرریسکترند.
تغییرات نیازمند دقت بیشتر:
- حذف ستون
- تغییر Type ستون
- اجباری کردن ستون دارای مقدار Null
- افزودن Unique Constraint روی داده موجود
- تغییر Relation
- تغییر رفتار Cascade
- تغییر نام جدول یا فیلد
- Migration روی جدول بسیار بزرگ
برای اجباری کردن یک فیلد جدید بهتر است از فرایند چندمرحلهای استفاده کنید:
- فیلد Nullable اضافه شود.
- برنامه جدید مقدار آن را برای رکوردهای تازه بنویسد.
- دادههای قبلی Backfill شوند.
- نبود مقدار بررسی شود.
- فیلد در Migration بعدی Required شود.
فایل SQL تولیدشده توسط Migration را پیش از Production بررسی کنید.
Backup قبل از Migration
Migration جایگزین Backup نیست. پیش از تغییرات مهم در Production:
- Backup معتبر بگیرید.
- امکان Restore را آزمایش کنید.
- Migration را روی نسخه مشابه داده آزمایش کنید.
- زمان اجرای Migration را تخمین بزنید.
- Rollback یا Forward Fix را برنامهریزی کنید.
- تغییرات ساختاری و انتشار کد را هماهنگ کنید.
اشتباهات رایج در Prisma
اجرای migrate dev در Production
فرمان درست برای Production:
npx prisma migrate deploy
فراموش کردن prisma generate
بعد از تغییر Schema:
npx prisma generate
ساخت Prisma Client در هر Request
این کار میتواند Connectionهای زیادی ایجاد کند. یک Client مشترک بسازید.
بازگرداندن مستقیم مدل دیتابیس
Response API را با select یا DTO مشخص کنید.
اعتماد به TypeScript برای ورودی Runtime
ورودی Request باید با Zod یا ابزار مشابه اعتبارسنجی شود.
دریافت تمام Relationها
استفاده بیمحدودیت از include ممکن است Response بزرگ و Query سنگین ایجاد کند.
نبود Index
Query Type-Safe همچنان میتواند کند باشد. Index براساس الگوی Query لازم است.
قرار دادن درخواست شبکه داخل Transaction
Transaction را کوتاه نگه دارید و عملیات طولانی شبکه را خارج آن اجرا کنید.
استفاده از db push در Workflow Production
برای پروژه تیمی و Production، Migrationهای نسخهبندیشده انتخاب مناسبتری هستند.
ذخیره Connection String در Git
فایل .env و Secretهای محیط Production نباید وارد مخزن شوند.
حذف داده بدون بررسی Relationها
پیش از delete و deleteMany اثر Cascade و وابستگی رکوردها را بررسی کنید.
چکلیست Prisma برای Production
- نسخه Prisma و Driver Adapter مشخص و Pin شدهاند.
DATABASE_URLاز Secret Manager یا متغیر محیطی دریافت میشود.- Prisma Client در Build تولید میشود.
- Migrationها داخل Git ثبت شدهاند.
migrate deployدر فرایند Deployment اجرا میشود.- پیش از Migrationهای حساس Backup گرفته میشود.
- یک Prisma Client مشترک استفاده میشود.
- Connection Pooling متناسب با Runtime تنظیم شده است.
- ورودیهای API پیش از Query اعتبارسنجی میشوند.
- خروجی API با
selectمحدود شده است. - Relationهای حجیم صفحهبندی میشوند.
- Queryهای پرتکرار Index مناسب دارند.
- Transactionها کوتاه هستند.
- APIهای خارجی داخل Transaction طولانی اجرا نمیشوند.
- Queryهای مجوز دسترسی،
userIdیا Tenant را بررسی میکنند. - دیتابیس Test از Production جدا است.
- Logها Connection String و داده حساس را ذخیره نمیکنند.
- Prisma Studio به اینترنت عمومی باز نیست.
- Monitoring برای خطا، زمان Query و مصرف Connection وجود دارد.
پرسشهای متداول
Prisma ORM چیست؟
Prisma ORM ابزاری برای مدلسازی داده، تولید Prisma Client Type-Safe، اجرای Query، مدیریت Relationها و ساخت Migration در پروژههای JavaScript و TypeScript است.
آیا Prisma جایگزین PostgreSQL است؟
خیر. PostgreSQL دیتابیس است و Prisma لایه دسترسی برنامه به آن محسوب میشود.
آیا برای استفاده از Prisma باید SQL بلد باشیم؟
برای شروع میتوان بدون نوشتن SQL زیاد پیش رفت، اما برای طراحی دیتابیس، Index، بهینهسازی، Transaction و رفع مشکلات Production باید SQL و مفاهیم دیتابیس را بدانید.
Prisma از چه دیتابیسهایی پشتیبانی میکند؟
Prisma ORM از چند دیتابیس رایج مانند PostgreSQL، MySQL، SQLite، SQL Server، CockroachDB و MongoDB پشتیبانی میکند. قابلیتها و محدودیتها ممکن است میان Connectorها متفاوت باشند.
تفاوت prisma generate و migrate چیست؟
prisma generate کد Prisma Client را تولید میکند. migrate ساختار دیتابیس را تغییر میدهد. این دو عملیات هدف متفاوتی دارند.
تفاوت prisma db push و migrate dev چیست؟
db push Schema را بدون ساخت تاریخچه Migration اعمال میکند. migrate dev Migration نسخهبندیشده تولید میکند و برای Workflow پروژه واقعی مناسبتر است.
آیا Prisma برای پروژه هوش مصنوعی مناسب است؟
بله. Prisma برای ذخیره کاربران، مکالمات، پیامها، Jobها، حافظه Agent، گزارش مصرف و Metadata مناسب است. برای Vector Search ممکن است نیاز به قابلیتهای PostgreSQL یا Queryهای اختصاصی نیز داشته باشید.
آیا میتوان با Prisma SQL خام اجرا کرد؟
بله، Prisma از $queryRaw و $executeRaw پشتیبانی میکند. Query باید پارامتری و با دقت اجرا شود.
آیا Prisma Client را در هر Request بسازیم؟
خیر. در بیشتر Backendها باید یک Client مشترک برای فرایند برنامه ساخته شود.
آیا migrate dev را روی سرور Production اجرا کنیم؟
خیر. برای اعمال Migrationهای موجود در Production از prisma migrate deploy استفاده کنید.
آیا Prisma Validation ورودی را انجام میدهد؟
Prisma Type و ساختار Query را بررسی میکند، اما جایگزین Runtime Validation ورودی کاربران نیست. برای req.body، Query String و پاسخ API از Zod یا ابزار مشابه استفاده کنید.
آیا Prisma برای Serverless مناسب است؟
قابل استفاده است، اما باید Connection Pooling، تعداد Instanceها، محدودیت Connection دیتابیس و الگوی ساخت Prisma Client متناسب با Platform بررسی شوند.
Prisma Studio چیست؟
یک رابط گرافیکی برای مشاهده و ویرایش دادههای دیتابیس است که بیشتر در محیط Development استفاده میشود.
جمعبندی
Prisma ORM یکی از ابزارهای کاربردی اکوسیستم TypeScript برای ارتباط با دیتابیس است. این ابزار با تبدیل Prisma Schema به یک Client اختصاصی، Queryهای Type-Safe، Autocomplete، Relation، Migration و Transaction را در اختیار توسعهدهنده قرار میدهد.
برای راهاندازی درست Prisma با PostgreSQL باید میان چند بخش تفاوت قائل شوید:
- Prisma Schema ساختار مدلها را تعریف میکند.
- Prisma Client Queryهای Type-Safe را اجرا میکند.
- Prisma Migrate تغییرات دیتابیس را نسخهبندی میکند.
- Driver Adapter اتصال Prisma 7 به PostgreSQL را فراهم میکند.
- Prisma Studio مشاهده دادههای Development را آسانتر میکند.
در یک پروژه واقعی، Type Safety بهتنهایی کافی نیست. ورودی API را اعتبارسنجی کنید، Queryها را با select محدود کنید، Indexها را براساس الگوی استفاده بسازید، Transactionها را کوتاه نگه دارید و Migrationهای Production را با Backup و برنامه استقرار کنترلشده اجرا کنید.
برای برنامههای هوش مصنوعی نیز Prisma میتواند لایه مناسبی برای نگهداری کاربران، مکالمات، پیامها، وضعیت درخواستها و گزارش مصرف باشد. اتصال به مدلهای هوش مصنوعی از طریق API درواره انجام میشود و Prisma دادههای برنامه را در PostgreSQL مدیریت میکند. این جداسازی، معماری پروژه را شفافتر و قابل نگهداریتر میسازد.
منابع تکمیلی
- مستندات رسمی Prisma
- راهنمای Prisma ORM با PostgreSQL
- مستندات Prisma Client
- راهنمای CRUD در Prisma
- راهنمای Relation Queryها
- راهنمای Transaction در Prisma
- مدیریت Connectionهای دیتابیس
- راهنمای ارتقا به Prisma ORM 7
- راهنمای Prisma و Next.js
مقالات مرتبط
- آموزش کامل PostgreSQL با Python، FastAPI و JSONB
- هوش مصنوعی با Node.js؛ ساخت اپلیکیشن AI با Express و API
- آموزش کامل REST API و طراحی RESTful
- آموزش کامل JSON در Python، JavaScript و API هوش مصنوعی
- ساخت چتبات هوش مصنوعی با Next.js، React و API درواره
- حافظه AI Agent با PostgreSQL، Redis و Vector Database
- ساخت Migration دیتابیس PostgreSQL با SQLAlchemy و Alembic
- آموزش ساخت Microservices با Python، FastAPI و Docker
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.