Database Migrations (Prisma)
Muammodan boshlaylik
Prisma bilan loyiha yozdingiz. Schema.prisma da model yozdingiz. Lekin schema o'zgarganda ma'lumotlar bazasiga qanday yetib boradi?
Ikkita yo'l bor:
prisma db push prisma migrate dev
──────────────────────────────────────────────────────
Schema ni to'g'ridan-to'g'ri │ Migration fayl yozib,
bazaga qo'llaydi │ keyin qo'llaydi
Tez, lekin tarix saqlanmaydi │ Sekinroq, lekin tarix bor
Development sinab ko'rish │ Haqiqiy loyiha uchun
prototip uchun │ (development + production)
Migration fayllar yaratilmaydi│ Har o'zgarish — alohida fayl
Real loyihalarda migration ishlatiladi. Nima uchun? Chunki:
- Har bir o'zgarish git da saqlanadi — kim, qachon, nima o'zgartirgan
- Production ga xavfsiz qo'llash mumkin — test qilingan SQL
- Bir nechta dasturchi ishlasa — barchaning bazasi bir xil bo'ladi
- Xato bo'lsa — orqaga qaytarish mumkin
Prisma migration qanday ishlaydi?
schema.prisma o'zgardi
│
▼
prisma migrate dev
│
├─► 1. Baza holati bilan schemani taqqoslaydi
│
├─► 2. Farqni SQL sifatida yozadi
│ prisma/migrations/20260101_add_phone/migration.sql
│
├─► 3. SQL ni bazaga qo'llaydi
│
└─► 4. Prisma Client qayta generatsiya qiladi
_prisma_migrations jadvali (PostgreSQL da avtomatik yaratiladi)
┌──────┬────────────────────────────────────┬──────────┬────────────┐
│ id │ migration_name │ finished │ applied_at │
├──────┼────────────────────────────────────┼──────────┼────────────┤
│ 1 │ 20260101000000_init │ true │ 2026-01-01 │
│ 2 │ 20260115000000_add_phone_to_users │ true │ 2026-01-15 │
│ 3 │ 20260120000000_create_posts_table │ true │ 2026-01-20 │
└──────┴────────────────────────────────────┴──────────┴────────────┘
Prisma bu jadvalga qarab: "qaysi migrationlar bajarilgan?" deb biladi.
Yangi fayllar — bajarilmagan, ularni ishga tushiradi.
Asosiy buyruqlar
Beshta asosiy buyruq bor. Har birini tushuntiramiz.
1. migrate dev — development uchun
npx prisma migrate dev --name add_phone_to_users
Nima qiladi:
- Hozirgi schema va baza holatini taqqoslaydi
- Farqni SQL faylga yozadi
- SQL ni bazaga qo'llaydi
prisma generate— Client qayta yaratiladi
Fayl yaratiladi:
prisma/migrations/20260115120000_add_phone_to_users/migration.sql
─────────────────┬─────────────────────────────────┬────────────
│ timestamp (avtomatik) │ --name dagi nom
Migration fayl ichida — Prisma yozgan SQL:
-- prisma/migrations/20260115120000_add_phone_to_users/migration.sql
-- AlterTable
ALTER TABLE "users" ADD COLUMN "phone" TEXT;
💡
--name— migration nomini ko'rsatadi. Qisqa, tushuntiruvcchi yozing:add_phone_to_users,create_posts_table,rename_name_to_fullname. Bu nom fayl nomida va git tarixida ko'rinadi.
2. migrate deploy — production uchun
npx prisma migrate deploy
Nima qiladi:
- Bajarilmagan migrationlarni topadi
- Ularni tartib bilan bazaga qo'llaydi
prisma generatebajarMaydi — alohida kerak
migrate dev dan farqi — interaktiv emas, savol bermaydi. CI/CD da ishlatish uchun mo'ljallangan.
⚠️ Production da
migrate devemas,migrate deployishlatiladi.migrate devdevelopment bazasini tozalashi mumkin.
3. migrate status — holat
npx prisma migrate status
Natija:
3 migrations found in prisma/migrations
✔ 20260101000000_init
✔ 20260115000000_add_phone_to_users
✗ 20260120000000_create_posts_table ← bajarilmagan (kutmoqda)
1 migration not yet applied.
Run `prisma migrate deploy` to apply.
4. migrate reset — bazani tozalash
npx prisma migrate reset
Nima qiladi:
- Bazadagi barcha jadvallarni o'chiradi
- Barcha migrationlarni boshidan bajaradi
prisma db seed— seed ma'lumotlar (bo'lsa)
⚠️ Faqat development da. Production da hech qachon — barcha ma'lumotlar yo'qoladi.
Qachon kerak? O'rganish jarayonida chalkashib ketganda, yoki migration fayllar baza bilan mos kelmay qolganda bazani "sifr" dan boshlash uchun.
5. db pull — mavjud bazadan schema olish
npx prisma db pull
Mavjud PostgreSQL bazasidagi jadvallardan schema.prisma ni avtomatik yaratadi. Eski loyihaga Prisma qo'shayotganda yoki boshqa ORM dan o'tishda foydali.
Amaliy jarayon — bosqichma-bosqich
Keling, yangi loyihadan boshlab migration qanday ishlashini ko'ramiz.
Qadam 1: Dastlabki schema
// prisma/schema.prisma
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now())
@@map("users")
}
npx prisma migrate dev --name init
prisma/migrations/
└── 20260101000000_init/
└── migration.sql
-- migration.sql
CREATE TABLE "users" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
users jadvali yaratildi:
┌────┬──────┬───────┬───────────┐
│ id │ name │ email │ createdAt │
└────┴──────┴───────┴───────────┘
Qadam 2: Yangi maydon qo'shish
model User {
id Int @id @default(autoincrement())
name String
email String @unique
phone String? // ← yangi, nullable
role Role @default(USER) // ← yangi, enum
createdAt DateTime @default(now())
@@map("users")
}
enum Role {
USER
ADMIN
}
npx prisma migrate dev --name add_phone_and_role_to_users
-- migration.sql (Prisma yozdi)
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN');
ALTER TABLE "users"
ADD COLUMN "phone" TEXT,
ADD COLUMN "role" "Role" NOT NULL DEFAULT 'USER';
users jadvali yangilandi:
┌────┬──────┬───────┬───────┬────────┬───────────┐
│ id │ name │ email │ phone │ role │ createdAt │
├────┼──────┼───────┼───────┼────────┼───────────┤
│ 1 │ Ali │ali@.. │ NULL │ USER │ 2026-01-01│ ← eski ma'lumotlar saqlanib qoldi
└────┴──────┴───────┴───────┴────────┴───────────┘
Qadam 3: Yangi jadval qo'shish
model Post {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
authorId Int
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
@@map("posts")
}
// User modeliga ham qo'shamiz
model User {
// ...
posts Post[]
}
npx prisma migrate dev --name create_posts_table
-- migration.sql
CREATE TABLE "posts" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorId" INTEGER NOT NULL,
CONSTRAINT "posts_pkey" PRIMARY KEY ("id")
);
ALTER TABLE "posts"
ADD CONSTRAINT "posts_authorId_fkey"
FOREIGN KEY ("authorId")
REFERENCES "users"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
Migration papkasi hozir:
prisma/migrations/
├── 20260101000000_init/
│ └── migration.sql ← users jadvali
├── 20260115000000_add_phone_and_role_to_users/
│ └── migration.sql ← phone va role ustunlari
└── 20260120000000_create_posts_table/
└── migration.sql ← posts jadvali
Ma'lumot yo'qotmasdan ustun o'zgartirish
Eng ko'p uchraydigan holat — ustun nomini o'zgartirish. Prisma bu yerda ehtiyotkor.
// name → fullName ga o'zgartirmoqchisiz
model User {
// ...
fullName String // ← name o'rniga
}
npx prisma migrate dev --name rename_name_to_fullname
Prisma shunday savol beradi:
⚠️ We need to reset the PostgreSQL database "mydb".
The following migration(s) are not applied:
...
? Are you sure you want to reset your database? (y/N)
Yoki migration SQL ni ko'rsatadi:
-- Prisma avtomatik yozgan SQL — bu ma'lumot yo'qotadi!
ALTER TABLE "users" DROP COLUMN "name"; ← eski ma'lumot yo'qoladi
ALTER TABLE "users" ADD COLUMN "fullName" TEXT NOT NULL; ← bo'sh
To'g'ri yechim — migration faylini qo'lda tahrirlash:
# Avval migration faylini yarating (--create-only — bazaga qo'llamaydi)
npx prisma migrate dev --name rename_name_to_fullname --create-only
Yaratilgan faylni ochasiz va qo'lda to'g'irlaysiz:
-- prisma/migrations/20260125000000_rename_name_to_fullname/migration.sql
-- 1. Yangi ustun qo'shamiz
ALTER TABLE "users" ADD COLUMN "fullName" TEXT;
-- 2. Eski ma'lumotni ko'chiramiz
UPDATE "users" SET "fullName" = "name";
-- 3. fullName ni NOT NULL qilamiz (ma'lumot ko'chirilgandan keyin)
ALTER TABLE "users" ALTER COLUMN "fullName" SET NOT NULL;
-- 4. Eski ustunni o'chiramiz
ALTER TABLE "users" DROP COLUMN "name";
Keyin bazaga qo'llaymiz:
npx prisma migrate dev
users jadvali:
Oldin: Keyin:
┌────┬────────┬───────┐ ┌────┬──────────┬───────┐
│ id │ name │ email │ │ id │ fullName │ email │
├────┼────────┼───────┤ ├────┼──────────┼───────┤
│ 1 │ Ali │ali@.. │ │ 1 │ Ali │ali@.. │ ← ma'lumot saqlanib qoldi
│ 2 │ Vali │vali@.│ │ 2 │ Vali │vali@.│
└────┴────────┴───────┘ └────┴──────────┴───────┘
Production da migration
CI/CD pipeline da
# deploy.sh yoki GitHub Actions da
echo "Prisma Client generatsiya..."
npx prisma generate
echo "Migrationlar qo'llanmoqda..."
npx prisma migrate deploy
echo "Ilova ishga tushmoqda..."
npm run start:prod
NestJS ilovasi ichida avtomatik
// main.ts
import { PrismaClient } from '@prisma/client';
async function bootstrap() {
// Ilova ishga tushishidan OLDIN migrationlarni bajaring
const prisma = new PrismaClient();
await prisma.$executeRaw`SELECT 1`; // ulanishni tekshirish
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
Yoki PrismaService da:
// prisma/prisma.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { execSync } from 'child_process';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
// Production da migration avtomatik bajaring
if (process.env.NODE_ENV === 'production') {
execSync('npx prisma migrate deploy', { stdio: 'inherit' });
}
await this.$connect();
}
}
Seed — boshlang'ich ma'lumotlar
Seed — yangi o'rnatilgan yoki reset qilingan bazaga namuna ma'lumotlar yozish. Test uchun admin user, kategoriyalar va h.k.
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
// Admin user yaratamiz
const admin = await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: {},
// upsert — bor bo'lsa hech narsa qilmaydi (update bo'sh)
// yo'q bo'lsa — create qiladi
create: {
name: 'Admin',
email: 'admin@example.com',
password: await bcrypt.hash('admin123', 10),
role: 'ADMIN',
},
});
// Kategoriyalar yaratamiz
const categories = await Promise.all([
prisma.category.upsert({
where: { name: 'NestJS' },
update: {},
create: { name: 'NestJS' },
}),
prisma.category.upsert({
where: { name: 'TypeScript' },
update: {},
create: { name: 'TypeScript' },
}),
]);
console.log('Seed muvaffaqiyatli bajarildi');
console.log({ admin, categories });
}
main()
.catch(console.error)
.finally(() => prisma.$disconnect());
package.json ga seed konfiguratsiyasi:
{
"prisma": {
"seed": "ts-node prisma/seed.ts"
},
"scripts": {
"db:seed": "prisma db seed"
}
}
# Seed ni ishga tushirish
npx prisma db seed
# migrate reset seed ni avtomatik ishga tushiradi
npx prisma migrate reset
Barcha buyruqlarni bir joyda
// package.json — foydali skriptlar
{
"scripts": {
"prisma:migrate": "prisma migrate dev",
"prisma:migrate:deploy": "prisma migrate deploy",
"prisma:migrate:status": "prisma migrate status",
"prisma:migrate:reset": "prisma migrate reset",
"prisma:generate": "prisma generate",
"prisma:seed": "prisma db seed",
"prisma:studio": "prisma studio",
"prisma:pull": "prisma db pull"
}
}
Buyruq Qachon ishlatiladi
─────────────────────────────────────────────────────────────────
migrate dev Schema o'zgarganda (development)
migrate deploy Production ga deploy qilganda
migrate status Qaysi migration bajarilganini ko'rish
migrate reset Bazani sifr dan boshlash (faqat dev)
generate schema.prisma o'zgarganda Client yangilash
db seed Boshlang'ich ma'lumotlar yozish
studio Vizual interfeys — brauzerda baza ko'rish
db pull Mavjud bazadan schema olish
TypeORM migration vs Prisma migration
┌──────────────────────┬────────────────────────┬───────────────────────────┐
│ │ TypeORM │ Prisma │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Migration yaratish │ migration:generate │ prisma migrate dev │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Migration bajarish │ migration:run │ prisma migrate deploy │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Orqaga qaytarish │ migration:revert │ Yo'q (qo'lda yozish kerak)│
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ SQL kim yozadi? │ TypeORM (generate) yoki│ Prisma o'zi yozadi │
│ │ dasturchi (create) │ │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Client generatsiya │ Shart emas │ Har migration da kerak │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Seed │ Alohida skript │ prisma db seed │
├──────────────────────┼────────────────────────┼───────────────────────────┤
│ Baza tozalash │ schema drop + run │ migrate reset │
└──────────────────────┴────────────────────────┴───────────────────────────┘
Prisma migration ning afzalligi: SQL ni o'zi yozadi — siz faqat schemani o'zgartirасиз. TypeORM da generate to'g'ri SQL yozmasa — qo'lda tuzatish kerak.
Prisma migration ning kamchiligi: Orqaga qaytarish (revert) yo'q. Agar migration xato bo'lsa — yangi migration yozib tuzatish kerak.
Vazifalar
1-vazifa — Amaliy:
schema.prisma ga Category modeli qo'shing (id, name unique, createdAt). Migration yarating va bajaring. Keyin Post ga categoryId qo'shing (One-to-Many). Yangi migration yarating.
2-vazifa — Ma'lumot saqlash:
Post modelida title ustunini headline ga o'zgartirmoqchisiz. --create-only bilan migration yarating va SQL ni qo'lda to'g'irlang — ma'lumot yo'qolmasin.
3-vazifa — Seed:
prisma/seed.ts yozing: 1 ta admin user va 3 ta kategoriya (NestJS, TypeScript, PostgreSQL) yaratsin. upsert ishlatsin — ikki marta ishga tushirganda dublikat bo'lmasin.
Xulosa
migrate dev — development da schema o'zgarganda. SQL yozadi, bazaga qo'llaydi, Client yangilaydi.
migrate deploy — production da. Faqat qo'llaydi, savol bermaydi.
--create-only — SQL ni qo'lda tahrirlash kerak bo'lganda. Avval fayl yaratadi, keyin alohida qo'llash mumkin.
Ma'lumot yo'qotmaslik — ustun o'zgartishda: --create-only → SQL ni tuzatish → migrate dev.
Seed — boshlang'ich ma'lumotlar. upsert ishlatiladi — ikki marta ishga tushirganda muammo bo'lmasin.
Prisma vs TypeORM: Prisma SQL ni o'zi yozadi (qulay), lekin revert yo'q. TypeORM da revert bor, lekin SQL qo'lda yozish kerak bo'lishi mumkin.
Keyingi dars
22-dars: Mongoose + MongoDB