IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis30/65-dars16 daqiqa
Mundarija (29)

Drizzle relations va drizzle-kit: db.query, tranzaksiya, generate, migrate va push

Qisqacha: Drizzle'da bog'lanishlar relations() bilan e'lon qilinadi va db.query.foydalanuvchilar.findFirst({ with: { vazifalar: … } }) — Prisma'dagi include ga o'xshash, lekin bitta SQL (LEFT JOIN LATERAL + json_agg). Tranzaksiya — db.transaction(async (tx) => …), ichma-ichi — savepoint. Migratsiya vositasi drizzle-kit: generate sxema farqidan SQL fayl yasaydi, migrate uni qo'llaydi, push faylsiz to'g'ridan qo'llaydi. Biz topgan tuzoq: ustun nomi o'zgarsa, generate "nomi o'zgardimi yoki yangimi?" deb so'raydi va TTY'siz (CI'da) xato bilan to'xtaydi.

Bu darsda

  • relations() bilan bir-ko'p va ko'p-bir bog'lanishlarni e'lon qilasiz.
  • db.query…findMany({ with }) bilan ichma-ich ma'lumot olasiz va uning SQL'ini o'qiysiz.
  • Tranzaksiya, savepoint va izolyatsiya darajasini Drizzle'da yozasiz.
  • drizzle.config.ts ni yozib, generate, migrate, push, pull ni ishlatasiz.
  • generate va push farqini, rename tuzog'ini tushuntirasiz.

Oldin bilishingiz kerak: Drizzle ORM 1: sxema va so'rovlar, Prisma: nested write va tranzaksiyalar, Subquery va LATERAL, Bog'lanishlar: 1-1, 1-N va N-M.

1. Nega bu kerak?

Oldingi darsda Drizzle bilan bitta jadvaldan o'qidik va JOIN yozdik. JOIN natijasi esa tekis qatorlar: Azizning 3 ta izohli vazifasi bo'lsa, JOIN har izoh uchun alohida qator beradi va ularni "vazifa → izohlar ro'yxati" ko'rinishiga JavaScript'da o'zingiz yig'asiz.

API'ga esa ichma-ich JSON kerak: { ism, vazifalar: [{ matn, izohlar: [...] }] }. Prisma'da bu include edi. Drizzle'da — relational queries (db.query): siz bog'lanishni bir marta e'lon qilasiz, Drizzle ichma-ich natijani bitta SQL bilan yig'adi.

Ikkinchi savol — sxema o'zgarishi. Drizzle sxemasi .ts faylda. Yangi ustun qo'shsangiz, bazaga ALTER TABLE kim yozadi? drizzle-kit — Drizzle'ning migratsiya vositasi.

O'xshatish: oila daraxti. Har odamning "otasi" va "bolalari" bor — buni bir marta chizasiz (relations), keyin "Azizning hamma nevaralarini ko'rsat" degan savolga daraxtning o'zi javob beradi.

Chiqishlarni biz ishga tushirdik: PostgreSQL 17.6, drizzle-orm 0.45.4, drizzle-kit 0.31.11, pg 8.23.1, Node 24.21.0. Sxema — oldingi darsdagi sxema.ts.

2. relations() va db.query

2.1 Bog'lanishni e'lon qilish

ts
import { relations } from "drizzle-orm";

const foydalanuvchiRel = relations(foydalanuvchilar, ({ many }) => ({
  vazifalar: many(vazifalar),
}));
const vazifaRel = relations(vazifalar, ({ one, many }) => ({
  egasi: one(foydalanuvchilar, {
    fields: [vazifalar.egasiId], references: [foydalanuvchilar.id],
  }),
  izohlar: many(izohlar),
}));
const izohRel = relations(izohlar, ({ one }) => ({
  vazifa: one(vazifalar, {
    fields: [izohlar.vazifaId], references: [vazifalar.id],
  }),
}));

one — bitta bog'langan yozuv (vazifaning egasi), many — ko'p (foydalanuvchining vazifalari). fields/references faqat one tomonda: "qaysi ustun qaysiga ishora qiladi". many tomoni juftini o'zi topadi.

Muhim: relations() — bazadagi tashqi kalit emas. U faqat Drizzle'ning so'rov quruvchisi uchun xarita. FK cheklovi bazada .references() yoki SQL migratsiya bilan yaratiladi (Foreign key).

Klientga sxema va relation'lar beriladi:

ts
const db = drizzle(pool, {
  schema: {
    foydalanuvchilar, vazifalar, izohlar,
    foydalanuvchiRel, vazifaRel, izohRel,
  },
});

2.2 with — ichma-ich o'qish

ts
const r = await db.query.foydalanuvchilar.findFirst({
  where: eq(foydalanuvchilar.id, 1),
  columns: { ism: true },
  with: {
    vazifalar: {
      columns: { matn: true },
      where: and(isNull(vazifalar.ochirilganVaqt),
        sql`${vazifalar.izohlarSoni} > 0`),
      orderBy: [asc(vazifalar.id)],
      limit: 2,
      with: { izohlar: { columns: { matn: true } } },
    },
  },
});
text
{
  "ism": "Aziz Karimov",
  "vazifalar": [
    {
      "matn": "JavaScript kursining 26-qismini boshlash #o'qish",
      "izohlar": [
        { "matn": "Docker darslaridan boshladim" },
        { "matn": "psql buyruqlari esda qolmayapti — \\? yordam beradi" },
        { "matn": "JOIN tushunarli bo'ldi" }
      ]
    },
    {
      "matn": "Postgres'ni Docker'da ko'tarish #o'qish #docker",
      "izohlar": [{ "matn": "Port 5432 band ekan, 5499 ga o'tdim" }]
    }
  ]
}

(Bo'sh qatorlar qisqartirildi.) Uch daraja: foydalanuvchi → vazifalar → izohlar. SQL — bitta. U uzun; faqat ikki marta uchraydigan left join lateral va json_agg so'zlarini toping:

text
SQL: select "foydalanuvchilar"."ism", "foydalanuvchilar_vazifalar"."data" as "vazifalar" from "foydalanuvchilar" "foydalanuvchilar" left join lateral (select coalesce(json_agg(json_build_array("foydalanuvchilar_vazifalar"."matn", "foydalanuvchilar_vazifalar_izohlar"."data") order by "foydalanuvchilar_vazifalar"."id" asc), '[]'::json) as "data" from (select * from "vazifalar" "foydalanuvchilar_vazifalar" where (… "ochirilgan_vaqt" is null and … "izohlar_soni" > 0)) order by … limit $1) "foydalanuvchilar_vazifalar" left join lateral (select coalesce(json_agg(json_build_array("foydalanuvchilar_vazifalar_izohlar"."matn")), '[]'::json) as "data" from "izohlar" … where … "vazifa_id" = "foydalanuvchilar_vazifalar"."id") "foydalanuvchilar_vazifalar_izohlar" on true) "foydalanuvchilar_vazifalar" on true where "foydalanuvchilar"."id" = $2 limit $3 -- [2,1,1]

Nima bo'ldi? Har with darajasi — bitta LEFT JOIN LATERAL (Subquery va LATERAL), natija Postgres'da json_agg bilan yig'iladi. Prisma'ning relationLoadStrategy: "join" (preview) bilan qiladigan ishi Drizzle'da sukut (Prisma prodakshnda). Kanon o'lchovida (50 vazifa + izohlari) Drizzle bitta SQL bilan 1.61 ms oldi; pg qo'lda json_agg bilan — 1.34 ms.

db.query da where, orderBy, limit, columns — har darajada alohida. columns — Prisma'dagi select.

2.3 Ko'p-bir tomoni

ts
await db.query.vazifalar.findMany({
  where: eq(vazifalar.id, 16),
  columns: { matn: true },
  with: { egasi: { columns: { ism: true } } },
});
text
[ { matn: 'Bozordan meva olish #bozor', egasi: { ism: 'Dilnoza Rahimova' } } ]

one — obyekt (yoki null, agar bog'lanish ixtiyoriy bo'lsa), many — massiv.

flowchart LR
  F["foydalanuvchilar"] -- "many: vazifalar" --> V["vazifalar"]
  V -- "one: egasi" --> F
  V -- "many: izohlar" --> I["izohlar"]
  I -- "one: vazifa" --> V

Diagrammaga qarang: har bog'lanish ikki tomondan e'lon qilinadi — many va unga juft one. Shunda with ikkala yo'nalishda ishlaydi.

Tekshirib ko'ring: relations() e'lonini qo'shdingiz, lekin bazada FK yo'q. with ishlaydimi? Bazaga yetim izoh yozish mumkinmi?

Javob

with ishlaydi — u faqat fields/references bo'yicha JOIN yasaydi. Yetim izohni yozish ham mumkin bo'ladi: relations() hech narsani tekshirmaydi. Butunlikni faqat bazadagi FK cheklovi kafolatlaydi (Foreign key).

2.4 Drizzle 1.0 haqida

Drizzle 1.0 (2026-10-25 holatida RC) relation e'lonining yangi shaklini olib keladi — defineRelations (relational queries v2): hamma bog'lanishlar bitta joyda, where obyekt ko'rinishida. Biz barqaror 0.45.4 ni o'rgandik; 1.0 barqaror bo'lgach, g'oya o'sha qoladi — sintaksis o'zgaradi. Yangi loyihada versiyani package.json da aniq yozing.

3. Tranzaksiyalar

3.1 Hammasi yoki hech narsa

ts
await db.transaction(async (tx) => {
  await tx.insert(vazifalar).values({ egasiId: 2, matn: "Tx 1" });
  await tx.insert(izohlar)
    .values({ vazifaId: 999999, matn: "yo'q vazifaga izoh" });
});
text
xato: 23503 izohlar_vazifa_id_fkey
vazifalar soni o'zgardimi: false
SQL: begin -- []
SQL: insert into "vazifalar" (…) values (default, $1, $2, default, …) -- [2,"Tx 1"]
SQL: insert into "izohlar" ("id", "vazifa_id", "muallif_id", "matn") values (default, $1, default, $2) -- [999999,"yo'q vazifaga izoh"]
SQL: rollback -- []

Ikkinchi INSERT FK'ni buzdi (23503) — birinchisi ham qaytdi. Prisma'ning interaktiv $transaction i bilan bir xil (Prisma: nested write va tranzaksiyalar); farqi — Drizzle'da timeout yo'q, tx — oddiy pg ulanishi ustidagi tranzaksiya. Prisma'dagi BEGIN ko'rinmagan log'dan farqli o'laroq, bu yerda begin ham log'da.

3.2 Savepoint

ts
await db.transaction(async (tx) => {
  await tx.insert(vazifalar).values({ egasiId: 2, matn: "Tashqi" });
  await tx.transaction(async (ichki) => {
    await ichki.insert(vazifalar)
      .values({ egasiId: 2, matn: "Ichki" });
    ichki.rollback();
  }).catch(() => console.log("ichki bekor qilindi"));
});
text
ichki bekor qilindi
[ { matn: 'Tashqi' } ]
SQL: begin -- []
SQL: insert into "vazifalar" (…) -- [2,"Tashqi"]
SQL: savepoint sp1 -- []
SQL: insert into "vazifalar" (…) -- [2,"Ichki"]
SQL: rollback to savepoint sp1 -- []
SQL: commit -- []

Log'ni qadamlarga bo'lib o'qiymiz:

Qadam Tashqi tx Ichki (sp1) Bazada
1 begin, "Tashqi" yozildi — hali hech kim ko'rmaydi
2 — savepoint sp1, "Ichki" yozildi ikkalasi tranzaksiya ichida
3 — rollback to savepoint sp1 "Ichki" bekor, "Tashqi" qoldi
4 commit — faqat "Tashqi" saqlandi

Ichma-ich transaction — SAVEPOINT (Tranzaksiya asoslari): ichki qism bekor bo'ldi, tashqisi saqlandi. ichki.rollback() maxsus xato otadi — uni .catch bilan ushladik, aks holda tashqi tranzaksiya ham bekor bo'lardi.

3.3 Izolyatsiya va o'qish rejimi

ts
await db.transaction(async (tx) => { await tx.$count(vazifalar); }, {
  isolationLevel: "serializable", accessMode: "read only",
});
text
SQL: begin isolation level serializable read only -- []
SQL: select count(*) as count from "vazifalar"; -- []
SQL: commit -- []

Drizzle sozlamani to'g'ridan BEGIN ga yozdi. SERIALIZABLE bilan 40001 kelsa, butun funksiyani qayta urinish — sizning ishingiz (Deadlock va serialization failure).

4. drizzle-kit: migratsiyalar

4.1 drizzle.config.ts

drizzle-kit'ni yangi, bo'sh bazada sinaymiz — kichik "eslatmalar" ilovasi (kit/sxema.ts):

ts
export const foydalanuvchilar = pgTable("foydalanuvchilar", {
  id: bigint("id", { mode: "number" })
    .primaryKey().generatedAlwaysAsIdentity(),
  email: text("email").notNull().unique(),
  ism: text("ism").notNull(),
});

export const eslatmalar = pgTable("eslatmalar", {
  id: bigint("id", { mode: "number" })
    .primaryKey().generatedAlwaysAsIdentity(),
  egasiId: bigint("egasi_id", { mode: "number" }).notNull()
    .references(() => foydalanuvchilar.id, { onDelete: "cascade" }),
  matn: text("matn").notNull(),
  vaqt: timestamp("vaqt", { withTimezone: true }).notNull(),
}, (t) => [index("eslatmalar_egasi_vaqt_idx").on(t.egasiId, t.vaqt)]);

Konfiguratsiya fayli loyiha ildizida:

ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  dialect: "postgresql",
  schema: "./kit/sxema.ts",
  out: "./kit/drizzle",
  dbCredentials: { url: process.env.DATABASE_URL! },
});

schema — sxema fayli (yoki papka), out — migratsiyalar papkasi, dbCredentials — ulanish (faqat bazaga ulanadigan buyruqlar uchun). DATABASE_URL — muhit o'zgaruvchisidan, kodda parol yo'q.

4.2 generate — sxemadan SQL fayl

bash
npx drizzle-kit generate --name boshlangich
text
2 tables
eslatmalar 4 columns 1 indexes 1 fks
foydalanuvchilar 3 columns 0 indexes 0 fks

[✓] Your SQL migration file ➜ kit\drizzle\0000_boshlangich.sql 🚀

Yasalgan fayl (0000_boshlangich.sql):

sql
CREATE TABLE "eslatmalar" (
	"id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "eslatmalar_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
	"egasi_id" bigint NOT NULL,
	"matn" text NOT NULL,
	"vaqt" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "foydalanuvchilar" (
	"id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "foydalanuvchilar_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
	"email" text NOT NULL,
	"ism" text NOT NULL,
	CONSTRAINT "foydalanuvchilar_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "eslatmalar" ADD CONSTRAINT "eslatmalar_egasi_id_foydalanuvchilar_id_fk" FOREIGN KEY ("egasi_id") REFERENCES "public"."foydalanuvchilar"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "eslatmalar_egasi_vaqt_idx" ON "eslatmalar" USING btree ("egasi_id","vaqt");

(drizzle-kit chiqishi aynan; uzun qatorlar telefonda gorizontal aylantiriladi.) Oddiy, o'qiladigan SQL. --> statement-breakpoint — buyruqlar chegarasi. FK jadvallardan keyin alohida ALTER TABLE bilan qo'shildi — jadvallar tartibi muhim bo'lmasin. Fayl yonida meta/0000_snapshot.json (snapshot — sxemaning o'sha paytdagi to'liq nusxasi, JSON'da) va meta/_journal.json (migratsiyalar ro'yxati) paydo bo'ldi. Keyingi generate yangi sxemani oxirgi snapshot bilan solishtiradi — bazaga ulanmaydi.

4.3 migrate — qo'llash

bash
npx drizzle-kit migrate
text
[✓] migrations applied successfully!
text
       Schema       |           Name           |    Type
--------------------+--------------------------+-------------
 drizzle            | __drizzle_migrations     | table
 id |     hash     |  created_at
----+--------------+---------------
  1 | c908513d87d1 | 1792921190959

(psql chiqishidan qisqartirilgan.) drizzle-kit qo'llangan migratsiyalarni alohida drizzle sxemasidagi __drizzle_migrations jadvalida saqlaydi: fayl mazmunining hash'i va vaqti (millisekundlarda). Prisma'ning _prisma_migrations jadvalining o'rni (Prisma Migrate chuqur).

4.4 O'zgarish: yangi ustun

Sxemaga muhim: boolean("muhim").notNull().default(false) qo'shdik:

bash
npx drizzle-kit generate --name muhim
npx drizzle-kit migrate
npx drizzle-kit generate
text
[✓] Your SQL migration file ➜ kit\drizzle\0001_muhim.sql 🚀
ALTER TABLE "eslatmalar" ADD COLUMN "muhim" boolean DEFAULT false NOT NULL;
[✓] migrations applied successfully!
No schema changes, nothing to migrate 😴

Ikkinchi generate — o'zgarish yo'q. ADD COLUMN … DEFAULT false NOT NULL — PG 11+ da katta jadvalda ham tez (metadata), lekin volatile default (now() kabi funksiya) jadvalni qayta yozadi — Xavfli migratsiyalar darsida kanon 11.8 s to'siqni o'lchagan.

4.5 Tuzoq: nom o'zgarishi va TTY

Ustun nomini matn → sarlavha qildik. Sxemada bu "bitta ustun yo'qoldi, bitta yangi paydo bo'ldi" — drizzle-kit nima qilganingizni bilmaydi: nomini o'zgartirdingizmi (RENAME COLUMN, ma'lumot saqlanadi) yoki eskisini o'chirib yangisini qo'shdingizmi (DROP + ADD, ma'lumot yo'qoladi)? U sizdan interaktiv so'raydi. CI'dagidek, terminalsiz ishga tushirdik:

text
Error: Interactive prompts require a TTY terminal (process.stdin.isTTY or process.stdout.isTTY is false). This can happen when running in CI, piped input, or non-interactive shells.

Bu — yaxshi xato: jim DROP COLUMN dan ko'ra to'xtash yaxshiroq. Qoida: generate ni dasturchi o'z terminalida ishga tushiradi, savolga javob beradi, yasalgan SQL'ni o'qiydi va commit qiladi. CI va deploy faqat tayyor fayllarni migrate qiladi. Prod'da ustun nomini o'zgartirishning o'zi ham xavfli — eski ilova nusxalari eski nomni kutadi (Zero-downtime: expand-contract).

4.6 push — faylsiz

bash
DATABASE_URL=…/drzpush npx drizzle-kit push
text
[✓] Pulling schema from database...
[✓] Changes applied

push bazani o'qiydi, sxema bilan solishtiradi va farqni to'g'ridan qo'llaydi — fayl yo'q, tarix yo'q, review yo'q. Lokal prototip va test bazasi uchun qulay; prod uchun — generate + migrate.

Buyruq Bazaga ulanadimi Fayl yasaydimi Qayerda
generate yo'q (snapshot bilan) ha, SQL dasturchi terminali
migrate ha yo'q, qo'llaydi deploy / CI
push ha yo'q lokal prototip
pull ha ha, schema.ts mavjud bazadan boshlash

4.7 pull — mavjud bazadan sxema

Kanon drizzle-kit pull ni bizning vazifalar bazasiga ishlatgan (holat 11 sxemasi):

text
pgTable(: 7
check(: 10
pgPolicy(: 5
pgView(: 1
pgMaterializedView(: 1
generatedAlwaysAs: 6
index(: 12
uniqueIndex(: 1
exclude: 0
trigger: 0
function: 0

Drizzle Prisma'dan ko'p narsani ko'rdi: CHECK'lar, RLS siyosatlari, view, materialized view, generated ustun. Lekin EXCLUDE cheklovi, trigger va funksiyalar — yo'q. Kanon yana bir xatoni topdi: indeks op-class'lari noto'g'ri o'qilgan (egasi_id bigint uchun int4_ops), tsvector turi — unknown. Ya'ni pull natijasi — boshlang'ich qoralama, uni qo'lda tekshirish kerak.

4.8 Studio

npx drizzle-kit studio — ma'lumotlarni brauzerda ko'rish interfeysi (Drizzle hujjati bo'yicha local.drizzle.studio sahifasi lokal serveringizga ulanadi). Biz uni ochmadik — sinalmagan. Prisma Studio'dagidek: prod bazaga ulamang.

5. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
db.query.vazifalar — undefined drizzle(pool, { schema }) berilmagan schema ga jadval va relation'lar
with da relation topilmadi ikki tomondan e'lon qilinmagan many + juft one
Interactive prompts require a TTY generate rename savoli CI'da lokal terminalda generate
prod'da kutilmagan DROP COLUMN push ishlatilgan prod — faqat generate + migrate
ichki rollback() hammasini bekor qildi maxsus xato ushlanmagan .catch yoki xatoni ataylab otish
pull dagi indeks noto'g'ri op-class xato o'qilgan qo'lda tekshirish

6. Mashqlar

Mashqlar kurs/mashqlar/27/30-drizzle-kit/ da. 2-mashq yechimi Node 24 da tekshirilgan (bazasiz); 3-mashqni biz bazada ishga tushirdik.

1-mashq (oson): qaysi buyruq?

  • Sxema farqidan SQL fayl yasash — drizzle-kit
  • Fayllarni bazaga qo'llash — drizzle-kit
  • Faylsiz to'g'ridan qo'llash (faqat lokal) — drizzle-kit
  • Mavjud bazadan schema.ts yasash — drizzle-kit
Yechim
  • generate — snapshot bilan solishtiradi, bazaga ulanmaydi.
  • migrate — __drizzle_migrations ga yozadi.
  • push — tarixsiz.
  • pull — introspection; natijani tekshiring.

2-mashq (o'rta): qaysi migratsiyalar qo'llanadi?

drizzle-orm manba kodida (pg-core/dialect.js, 0.45.4) migrate shunday ishlaydi: __drizzle_migrations dan oxirgi qo'llangan created_at ni oladi va faqat when (fayl vaqti) undan katta migratsiyalarni qo'llaydi. Ikki funksiya yozing (bazasiz): drizzleQoidasi(journal, qollangan) — aynan shu qoida; toplamQoidasi(journal, qollangan) — "ro'yxatda bor, bazada yo'q" hammasi. Ikkalasi tag lar ro'yxatini idx tartibida qaytarsin. Jamoadagi holatni sinang: Ali 0002 ni qo'lladi, keyin Malikaning ertaroq vaqtli 0001 migratsiyasi main ga qo'shildi.

Yechim
js
const tartibla = (journal) =>
  journal.entries.toSorted((a, b) => a.idx - b.idx);

function drizzleQoidasi(journal, qollangan) {
  const oxirgi = Math.max(0, ...qollangan.map(Number));
  return tartibla(journal)
    .filter((e) => e.when > oxirgi)
    .map((e) => e.tag);
}

function toplamQoidasi(journal, qollangan) {
  const bor = new Set(qollangan.map(Number));
  return tartibla(journal)
    .filter((e) => !bor.has(e.when))
    .map((e) => e.tag);
}

const journal = {
  entries: [
    { idx: 0, when: 1000, tag: "0000_boshlangich" },
    { idx: 1, when: 1500, tag: "0001_malika_teg" },
    { idx: 2, when: 2000, tag: "0002_ali_muhim" },
  ],
};
const qollangan = ["1000", "2000"]; // 0001 hali qo'llanmagan
console.log(drizzleQoidasi(journal, qollangan));
console.log(toplamQoidasi(journal, qollangan));

Konsolda:

text
[]
[ '0001_malika_teg' ]

Oxirgi qo'llangan vaqt 2000 — Malikaning 1500 vaqtli migratsiyasi undan kichik, shuning uchun "oxirgidan keyingilar" qoidasi uni jim tashlab ketadi: bazada uning jadvali hech qachon paydo bo'lmaydi va xato ham chiqmaydi. To'plam qoidasi uni topadi. created_at bazadan satr bo'lib kelishi mumkin (bigint) — Number bilan solishtiramiz.

Amaliy xulosa: Drizzle bilan ishlaganda migratsiyalarni main ga vaqt tartibida qo'shing — boshqa branch'dagi eski migratsiyani merge qilgandan keyin uni generate bilan qayta yasang. Kanon holat 40 da (Migratsiyani deploy'da qo'llash) Prisma uchun to'plam qoidasi bilan tekshiruvni server ishga tushishiga qo'ygan.

3-mashq (qiyin): vazifa, egasi va izohlar soni

Sardor (3) ning birinchi uchta vazifasini (id bo'yicha) db.query bilan oling: har birida egasining ismi va izohlar. Chiqish: Ism: matn — N izoh. SQL soni 1 bo'lsin.

Yechim
ts
const r = await db.query.vazifalar.findMany({
  where: eq(vazifalar.egasiId, 3),
  columns: { matn: true },
  orderBy: [asc(vazifalar.id)],
  limit: 3,
  with: {
    egasi: { columns: { ism: true } },
    izohlar: { columns: { matn: true } },
  },
});
for (const v of r) {
  console.log(`${v.egasi.ism}: ${v.matn} — ${v.izohlar.length} izoh`);
}

Biz ishga tushirdik (PostgreSQL 17.6, drizzle-orm 0.45.4; SQL'larni logger bilan sanadik):

text
Sardor Tursunov: Mashinani texnik ko'rikka olib borish #mashina — 2 izoh
Sardor Tursunov: Moy almashtirish #mashina — 0 izoh
Sardor Tursunov: Hovlidagi daraxtlarni oqlash #uy — 0 izoh
SQL soni: 1

Ikki with (egasi — one, izohlar — many) — bitta SQL'da ikki LEFT JOIN LATERAL. v.egasi.ism turi string — egasiId NOT NULL bo'lgani uchun egasi null bo'lmaydi. Izohlar soni faqat kerak bo'lsa, butun izohlarni olish o'rniga extras bilan sql subquery yoki bazadagi izohlar_soni (counter cache) arzonroq.

7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

vazifalar-api Drizzle'ga va drizzle-kit'ga ko'chmaydi. Sabablar:

  1. Sxema manbai — SQL migratsiyalar. Loyihaning sxemasida trigger'lar, funksiyalar, EXCLUDE cheklovi, RLS bor. Kanon pull bilan ko'rsatdi: drizzle-kit EXCLUDE, trigger va funksiyalarni ko'rmaydi — generate ular haqida hech narsa bilmaydi. Prisma ham ularni ko'rmaydi. Shuning uchun kanonda migratsiya — qo'lda yozilgan SQL, Prisma Migrate esa ularni faqat qo'llaydi (Prisma Migrate chuqur, holat 36). drizzle-kit bilan ham aynan shunday bo'lardi — generate dan foyda kam.
  2. Bitta ORM — kanon qarori: Prisma asosiy, Drizzle — o'rganish uchun tajriba (ORM tanlash qo'llanmasi).

Lekin agar yangi loyihani noldan Drizzle bilan boshlasangiz va sxemangiz oddiy jadval, indeks va FK'lardan iborat bo'lsa — drizzle-kit'ning generate + migrate oqimi to'liq yetadi. Qo'shimcha SQL (trigger, CHECK) kerak bo'lsa, drizzle-kit generate --custom bo'sh SQL fayl yaratadi va unga o'zingiz yozasiz (Drizzle hujjati, sinalmagan).

8. Real ishda

  • Drizzle loyihalarida odatiy tuzilma: db/sxema.ts (jadvallar), db/relations.ts, drizzle.config.ts, drizzle/ (migratsiyalar, git'da). PR'da sxema o'zgarishi bilan birga yasalgan SQL fayl ham ko'rib chiqiladi.
  • NestJS'da Drizzle rasmiy modulsiz — custom provider bilan ulanadi (Drizzle va Kysely bilan NestJS, 30-qism).
  • Intervyuda: "push va migrate farqi?", "drizzle-kit rename'ni qanday aniqlaydi?", "Relational queries qanday SQL beradi?"

Xulosa

  • relations() — so'rov quruvchisi uchun xarita (FK emas); one + many ikki tomondan.
  • db.query…findMany({ with }) — ichma-ich natija bitta SQL'da (LEFT JOIN LATERAL + json_agg).
  • db.transaction — hammasi yoki hech narsa; ichma-ichi — savepoint; isolationLevel, accessMode to'g'ridan BEGIN ga.
  • drizzle-kit: generate (snapshot → SQL fayl), migrate (__drizzle_migrations), push (faylsiz, faqat lokal), pull (qoralama).
  • Rename — interaktiv savol; TTY'siz xato. generate — dasturchida, migrate — deploy'da.

Keyingi dars: Kysely va Knex query builder'lari — ORM'siz, lekin SQL satrlarisiz: tipli (Kysely) va klassik (Knex).

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Drizzle relations va drizzle-kit: db.query, tranzaksiya, generate, migrate va push — IlmHamroh