WisarWisar
Dasturlash kitobi/6-QISM — Database19 daqiqa

6.13-bob: ORM — TypeORM (entity, repository, relations)

6-QISM — Ma'lumotlar bazasi (Database) · 13-mavzu


1. Kirish va motivatsiya

Sequelize (klassik — 6.11) va Prisma (schema-first — 6.12) ni bildik. Endi uchinchi va oxirgi ORM — TypeORM ni o'rganamiz. TypeORM — yana boshqacha yondashuv: class va dekorator (decorator) asosida. Model — oddiy TypeScript class, dekoratorlar (@Entity, @Column) bilan bezatilgan. Bu — TypeScript bilan tabiiy, va eng muhimi: NestJS (8-QISM) bilan eng yaxshi ishlaydi (NestJS o'zi ham dekorator asosida). Sizning stack'ingda TypeORM bor, va NestJS qismiga (8) bu bob bevosita tayyorgarlik.

TypeORM — "TypeScript ORM" (nomidan ham). U class'larni jadvalga, obyektlarni qatorga moslaydi, lekin dekorator bilan (Prisma'ning alohida schema fayli yoki Sequelize'ning define o'rniga). Masalan, @Entity() class — jadval, @Column() xususiyat — ustun, @OneToMany() — bog'lanish. Bu — kod va model bir joyda (class ichida), TypeScript turlari bilan integratsiyalashgan.

TypeORM ikki asosiy ish uslubini beradi: Active Record (model'ning o'zida metodlar — user.save()) va Data Mapper (alohida repository — userRepository.save(user)). Data Mapper (repository pattern) — kattaroq loyihalar uchun afzal (toza arxitektura — 9), va NestJS standartda shuni qo'llaydi. Bu bob: entity, repository pattern, relations, query builder, va tranzaksiya — chuqur. NestJS'ga (8) o'tishdan oldin oxirgi DB bobi.

O'xshatish: Prisma — alohida arxitektura chizmasi (schema.prisma — bino loyihasi alohida qog'ozda). TypeORM — bino qismlariga yorliq yopishtirish (class'ning o'ziga @Entity, @Column dekoratorlari — "bu jadval", "bu ustun" deb belgilash). Ikkalasi ham bino quradi, lekin TypeORM — model va belgilar bir joyda (class ichida), kod bilan integratsiya.

Nega muhim?

  • NestJS bilan tabiiy — NestJS (8) dekorator asosida; TypeORM mos (8.3 tayyorgarligi).
  • TypeScript — class/dekorator, tur integratsiyasi.
  • Repository pattern — toza arxitektura (9: Clean Architecture).
  • Stack talabi — TypeORM sizning stack'ingda; uchchala ORM'ni bilish.

2. Nazariya — chuqur tushuntirish

2.1. TypeORM nima va dekorator yondashuvi

TypeORM — TypeScript/JavaScript uchun ORM, dekorator asosida (typeorm.io):

text
  Class (TS) + dekorator  jadval

  @Entity()            jadval (User class  "user" jadval)
  @Column()            ustun
  @PrimaryGeneratedColumn()  PK (avto)
  @OneToMany() / @ManyToOne()  bog'lanish

  Model = TypeScript class (turlar bilan); belgilar = dekorator

Dekorator (7.6: TS decorators) — class/xususiyatga "yorliq" qo'shuvchi maxsus sintaksis (@). TypeORM ularni o'qib, jadval/ustun/bog'lanishni biladi. Prisma'dan farq: alohida schema fayl yo'q — hammasi class ichida (kod bilan birga).

2.2. Active Record vs Data Mapper

TypeORM ikki ish uslubini beradi (typeorm.io):

text
  Active Record — model O'ZIDA metodlar:
  const user = new User(); user.ism = "Ali"; await user.save();
  await User.findOne({ where: { id: 1 } });
   sodda, kichik loyiha; lekin model + DB mantig'i aralash

  Data Mapper — alohida REPOSITORY:
  await userRepository.save(user);
  await userRepository.findOne({ where: { id: 1 } });
   toza ajratish (model = ma'lumot; repository = DB amali); KATTA loyiha, NestJS

Tanlov (typeorm/libeo): Data Mapper (repository) — afzal (toza arxitektura — 9, test oson, NestJS standarti — 8.3). Active Record — kichik/tez loyiha. Bu bobda asosan Data Mapper (repository pattern).

2.3. O'rnatish va DataSource

bash
npm install typeorm reflect-metadata    # ORM + dekorator metama'lumot
npm install pg                           # driver (yoki mysql2)
js
import "reflect-metadata";               // ENG TEPADA (dekorator uchun shart)
import { DataSource } from "typeorm";

export const AppDataSource = new DataSource({
  type: "postgres",                      // DB turi
  url: process.env.DATABASE_URL,         // (5.8)
  entities: [User, Order],               // model class'lar
  synchronize: false,                    //  production'da FALSE (2.13)
  logging: true,                         // SQL log (dev)
});

await AppDataSource.initialize();        // ulanish

reflect-metadata eng tepada import qilinishi shart (dekoratorlar metama'lumot uchun). DataSource — ulanish + sozlama (Prisma client / Sequelize instance ekvivalenti). synchronize — schema'ni avtomatik moslash (dev'da mumkin, production'da XAVFLI — 2.13).

2.4. Entity (jadval) — dekorator bilan

ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";

@Entity("users")                          // jadval nomi
export class User {
  @PrimaryGeneratedColumn()               // PK, avto (6.4: SERIAL)
  id: number;

  @Column({ length: 50 })                 // VARCHAR(50)
  ism: string;

  @Column({ unique: true })               // UNIQUE
  email: string;

  @Column({ select: false })              // default'da qaytarilmaydi (parol — 14)
  parol: string;

  @Column({ type: "enum", enum: ["user", "admin"], default: "user" })
  rol: string;

  @CreateDateColumn()                     // createdAt avtomatik
  createdAt: Date;

  @UpdateDateColumn()                     // updatedAt avtomatik (6.2)
  updatedAt: Date;
}

Entity dekoratorlari: @Entity (jadval), @PrimaryGeneratedColumn (PK), @Column (ustun + sozlama), @CreateDateColumn/@UpdateDateColumn (avtomatik vaqt). Class — oddiy TS (turlar bilan); dekorator — DB metama'lumoti. Toza, o'qiladigan.

2.5. Column turlari va sozlamalari

ts
@Column({ type: "varchar", length: 100 })          // VARCHAR
@Column({ type: "decimal", precision: 10, scale: 2 })   // DECIMAL (pul — 6.4: 2.3)
@Column({ type: "text", nullable: true })          // ixtiyoriy
@Column({ type: "boolean", default: true })
@Column({ type: "jsonb", nullable: true })         // JSONB (6.6: 2.10)
@Column("simple-array")                             // massiv
@Column({ type: "timestamptz" })

@Column sozlamalari: type (DB tur), length, nullable, default, unique, select. precision/scale (DECIMAL — pul). TypeScript turi (string, number) + DB turi (type) — ikkalasi.

2.6. Repository (DB amallari)

Repository — entity bilan ishlash (CRUD — Data Mapper — 2.2):

js
const userRepo = AppDataSource.getRepository(User);

// CREATE
const user = userRepo.create({ ism: "Ali", email: "ali@a.uz" });   // obyekt yaratadi (saqlamaydi)
await userRepo.save(user);                          // saqlaydi (INSERT/UPDATE)

// READ
await userRepo.find();                              // hammasi
await userRepo.findOne({ where: { id: 1 } });       // bittasi
await userRepo.findOneBy({ email: "ali@a.uz" });    // qisqa
await userRepo.find({ where: { yosh: MoreThan(18) } });   // operator (2.8)

// UPDATE
await userRepo.update({ id: 1 }, { yosh: 26 });     // to'g'ridan
// yoki: user.yosh = 26; await userRepo.save(user);

// DELETE
await userRepo.delete({ id: 1 });
await userRepo.softDelete({ id: 1 });               // soft delete (@DeleteDateColumn bilan)

getRepository(Entity) — repository oladi. create (obyekt — saqlamaydi) + save (saqlaydi), yoki insert. find/findOne/findOneBy. update/delete/softDelete. Repository — barcha DB amali shu yerda (toza — 9).

2.7. Custom repository (toza arxitektura — 9)

Murakkab so'rovlar uchun custom repository (repository pattern — medium/anton):

js
// Repository'ni kengaytirib, maxsus metodlar (9: Clean Architecture)
export const UserRepository = AppDataSource.getRepository(User).extend({
  findFaollar() {
    return this.find({ where: { faol: true } });
  },
  findEmailBilan(email) {
    return this.createQueryBuilder("u")            // query builder (2.9)
      .addSelect("u.parol")                         // parolni ham (login uchun)
      .where("u.email = :email", { email })
      .getOne();
  },
});

Custom repository — entity'ga oid barcha so'rov mantiqini bir joyda (libeo). Biznes logika (service — 5.6) repository'ni ishlatadi (DB tafsilotini bilmaydi — 9: separation of concerns). NestJS'da bu — @InjectRepository 8.3-bob.

2.8. Operatorlar (Find Options)

js
import { MoreThan, LessThan, Like, In, Between, IsNull, Not } from "typeorm";

await userRepo.find({
  where: {
    yosh: MoreThan(18),                              // > 18
    rol: In(["admin", "moderator"]),                // IN
    ism: Like("Al%"),                               // LIKE
    email: Not(IsNull()),                           // NOT NULL
  },
  order: { yosh: "DESC" },                           // ORDER BY (6.4)
  take: 10, skip: 20,                                // limit, offset
  select: ["id", "ism", "email"],                    // SELECT ustunlar
  relations: { orders: true },                       // eager (JOIN — 2.10)
});

Find operatorlari: MoreThan/LessThan, Like, In, Between, IsNull, Not (funksiya sifatida — 6.4: 2.9). take/skip (sahifalash), order, select, relations (bog'lanish). Sequelize Op 6.11-bob / Prisma 6.12-bob ekvivalenti.

2.9. Query Builder (murakkab so'rov)

Murakkab so'rov uchun Query Builder (raw SQL'dan oldin — typeorm):

js
const natija = await userRepo
  .createQueryBuilder("u")                           // alias "u"
  .leftJoinAndSelect("u.orders", "o")                // JOIN (6.7)
  .where("u.yosh > :yosh", { yosh: 18 })             // parametrli (14)
  .andWhere("o.holat = :holat", { holat: "tugallandi" })
  .orderBy("u.createdAt", "DESC")
  .take(10)
  .getMany();

Query Builder — find options yetmaganda (JOIN, murakkab shart, agregat — 6.7). Parametrli (:yosh — injection himoyasi — 14). Raw SQL'ga o'tishdan oldingi qatlam (window/CTE uchun — baribir raw kerak bo'lishi mumkin — 2.14). Kuchli, lekin SQL bilim talab.

2.10. Relations (bog'lanishlar)

ts
// One-to-Many: User  Order (6.1: 2.5)
@Entity()
export class User {
  @PrimaryGeneratedColumn() id: number;
  @OneToMany(() => Order, (order) => order.user)     // bir user — ko'p order
  orders: Order[];
}

@Entity()
export class Order {
  @PrimaryGeneratedColumn() id: number;
  @ManyToOne(() => User, (user) => user.orders)      // ko'p order — bir user
  @JoinColumn({ name: "user_id" })                   // FK ustun nomi
  user: User;
}
ts
// Many-to-Many: @ManyToMany + @JoinTable (owning side)
@ManyToMany(() => Tag)
@JoinTable()                                          // junction jadval (owning side)
tags: Tag[];

Relation dekoratorlari (typeorm): @OneToOne, @OneToMany/@ManyToOne (juft), @ManyToMany. @JoinColumn — FK egasini belgilaydi (one-to-one/many-to-one); @JoinTable — many-to-many junction (faqat bir tomonda — owning side). Bog'lanish ikki tomonda ta'riflanadi.

2.11. Eager va lazy loading

js
// Eager (relations bilan — JOIN — 2.8)
await userRepo.find({ relations: { orders: true } });   // buyurtmalar birga

// Yoki entity'da default eager (ehtiyot — har doim yuklanadi)
@OneToMany(() => Order, o => o.user, { eager: true })

// Lazy (kerak bo'lganda — Promise)
@OneToMany(() => Order, o => o.user, { lazy: true })
orders: Promise<Order[]>;

Eager (relations — N+1 oldini oladi — 6.10: 2.11) — kerakli joyda. Entity'da eager: true — har doim yuklanadi (ehtiyot — ortiqcha). Lazyawait user.orders (kerak bo'lganda). Repository'da relations — eng moslashuvchan (so'rovga qarab).

2.12. Tranzaksiya (QueryRunner / transaction)

js
// 1. DataSource.transaction (sodda — managed)
await AppDataSource.transaction(async (manager) => {
  await manager.update(Account, { id: 1 }, { balans: () => "balans - 100" });
  await manager.update(Account, { id: 2 }, { balans: () => "balans + 100" });
  // xato  avtomatik rollback (6.9)
});

// 2. QueryRunner (to'liq nazorat)
const queryRunner = AppDataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
  await queryRunner.manager.save(order);
  await queryRunner.commitTransaction();
} catch (err) {
  await queryRunner.rollbackTransaction();
} finally {
  await queryRunner.release();                         // (6.5: 2.10)
}

Tranzaksiya — ikki usul: AppDataSource.transaction(async manager) (managed — sodda, afzal); QueryRunner (to'liq nazorat — lock, izolyatsiya — 6.9). manager — tranzaksiya ichidagi entity manager. NestJS'da DataSource inject qilinadi 8.3-bob.

2.13. synchronize va migration (muhim)

TypeORM synchronize: true — schema'ni avtomatik moslaydi (dev'da qulay, production'da XAVFLI):

text
  synchronize: true   entity o'zgarsa, DB AVTOMATIK moslanadi
   production'da: ma'lumot YO'QOLISHI mumkin (ustun o'chsa — data o'chadi!)
   dev'da: tez (lekin migration o'rganish yaxshiroq)

  Production: synchronize: false + MIGRATION (nazorat ostida — 6.14)

Production'da synchronize: false (typeorm, eng muhim ogohlantirish): true — entity o'zgarishini DB'ga avtomatik qo'llaydi, lekin nazoratsiz (ustun o'chsa — ma'lumot yo'qoladi!). Production'da migration 6.14-bob — versiyalangan, qaytariladigan. Migration: typeorm migration:generate/migration:run.

2.14. Raw query (murakkab — 2.9 yetmaganda)

js
// Query builder ham yetmasa — raw (parametrli — 14)
const natija = await AppDataSource.query(
  "SELECT u.ism, RANK() OVER (ORDER BY SUM(o.summa) DESC) AS reyting FROM users u ...",
  [/* parametrlar */]                                  // parametrli (injection — 14)
);

Query Builder 2.9-bob ko'p narsani qiladi, lekin window/murakkab CTE 6.7-bob — ba'zan AppDataSource.query (raw). Parametrli (injection himoyasi — 14). ORM + raw aralash — normal.

2.15. NestJS bilan (8 ko'prik)

text
  NestJS'da TypeORM 8.3-bob:
  - @nestjs/typeorm modul
  - TypeOrmModule.forRoot(...) — DataSource
  - TypeOrmModule.forFeature([User]) — entity'lar
  - @InjectRepository(User) — repository inject (DI — 8.2)
  - Entity dekoratorlari + NestJS dekoratorlari — tabiiy uyg'unlik

   TypeORM + NestJS = eng mashhur kombinatsiya (dekorator + DI)

TypeORM NestJS bilan eng tabiiy ishlaydi (ikkalasi dekorator + DI — 8.2). Bu bob — NestJS DB qismiga 8.3-bob bevosita tayyorgarlik. Repository pattern 2.7-bob NestJS'da inject qilinadi.

2.16. Prisma vs TypeORM (yana — tanlov)

text
  TypeORM tanla:
   NestJS loyiha (dekorator — tabiiy — 2.15)
   Active Record/Data Mapper moslashuvchanlik
   Mavjud TypeORM loyiha

  Prisma tanla 6.12-bob:
   Eng yaxshi type-safety, DX
   Yangi loyiha, schema-first
   Eng zamonaviy

  Ikkalasi ham yaxshi; NestJS'da — ko'pincha TypeORM (an'ana) yoki Prisma (zamonaviy)

2026'da NestJS'da ikkalasi ham ishlatiladi (nexumo): TypeORM (an'anaviy, dekorator) yoki Prisma (zamonaviy, type-safe). Tanlov — jamoa/loyiha. Uchchala ORM'ni bilish — har qanday loyihaga tayyorlik.

2.17. Xavfsizlik va best practices (14)

text
   Production'da synchronize: false + migration (2.13, 14) — ENG MUHIM
   Repository pattern (toza arxitektura — 2.7, 9)
   Relations (eager) o'ylab — N+1 oldini oling (2.11, 6.10)
   select: false maxfiy maydon (parol — 2.4, 14)
   Query builder/raw parametrli (injection — 2.9, 2.14, 14)
   Tranzaksiya (ko'p yozuv — managed — 2.12)
   reflect-metadata eng tepada 2.3-bob
   Base entity (umumiy ustunlar — DRY)

3. Sintaksis — tez ma'lumotnoma

ts
import "reflect-metadata";
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn() id: number;
  @Column() ism: string;
  @OneToMany(() => Order, o => o.user) orders: Order[];   // relation (2.10)
}
js
const repo = AppDataSource.getRepository(User);            // (2.6)
await repo.save(repo.create({...}));                       // C
await repo.findOne({ where: { id }, relations: { orders: true } });   // R (2.8, 2.11)
await repo.update({ id }, {...});  await repo.delete({ id });          // U, D
repo.createQueryBuilder("u").where("u.x = :x", { x }).getMany();       // query builder (2.9)
AppDataSource.transaction(async (m) => {...});            // tranzaksiya (2.12)

4. Batafsil kod namunalari

Misol 1 — DataSource (2.3)

ts
// data-source.ts
import "reflect-metadata";                              // eng tepada (2.3)
import { DataSource } from "typeorm";
import { User } from "./entities/User.js";
import { Order } from "./entities/Order.js";
import { config } from "./config/index.js";

export const AppDataSource = new DataSource({
  type: "postgres",
  url: config.databaseUrl,                              // (5.8)
  entities: [User, Order, Product],
  migrations: ["dist/migrations/*.js"],
  synchronize: false,                                  // production'da FALSE (2.13, 14)
  logging: config.isProd ? ["error"] : true,           // dev'da SQL log
});

export async function dbUlash() {
  await AppDataSource.initialize();
  console.log(" TypeORM ulandi");
}

Misol 2 — Entity (relations bilan — 2.4, 2.10)

ts
// entities/User.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn, BeforeInsert } from "typeorm";
import bcrypt from "bcrypt";
import { Order } from "./Order.js";

@Entity("users")
export class User {
  @PrimaryGeneratedColumn() id: number;

  @Column({ length: 50 }) ism: string;

  @Column({ unique: true }) email: string;

  @Column({ select: false }) parol: string;            // parolsiz (14, 2.4)

  @Column({ type: "enum", enum: ["user", "admin"], default: "user" }) rol: string;

  @OneToMany(() => Order, (order) => order.user) orders: Order[];   // (2.10)

  @CreateDateColumn() createdAt: Date;
  @UpdateDateColumn() updatedAt: Date;

  @BeforeInsert()                                       // hook (saqlashdan oldin — 6.2: 2.16)
  async hashParol() {
    this.parol = await bcrypt.hash(this.parol, 12);     // (5.15)
  }
}
ts
// entities/Order.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm";
import { User } from "./User.js";

@Entity("orders")
export class Order {
  @PrimaryGeneratedColumn() id: number;
  @Column({ type: "decimal", precision: 10, scale: 2 }) summa: number;   // pul (2.5)
  @ManyToOne(() => User, (user) => user.orders, { onDelete: "CASCADE" })  // (2.10, 6.6: 2.7)
  @JoinColumn({ name: "user_id" }) user: User;
}

Misol 3 — Custom repository (2.7)

ts
// repositories/UserRepository.ts
import { AppDataSource } from "../data-source.js";
import { User } from "../entities/User.js";

export const UserRepository = AppDataSource.getRepository(User).extend({
  // Login uchun (parol bilan — select: false ni override)
  findForLogin(email: string) {
    return this.createQueryBuilder("u")                // query builder (2.9)
      .addSelect("u.parol")                            // parolni ham
      .where("u.email = :email", { email })
      .getOne();
  },

  // Sahifalangan ro'yxat
  royxat({ page = 1, limit = 20, qidiruv }) {
    const qb = this.createQueryBuilder("u");
    if (qidiruv) qb.where("u.ism ILIKE :q", { q: `%${qidiruv}%` });   // qidiruv (2.9)
    return qb.orderBy("u.createdAt", "DESC")
      .take(limit).skip((page - 1) * limit)
      .getManyAndCount();                              // [ma'lumot, jami]
  },
});

Misol 4 — CRUD service (repository — 2.6)

js
import { AppDataSource } from "../data-source.js";
import { User } from "../entities/User.js";
import { UserRepository } from "../repositories/UserRepository.js";
import { MoreThan } from "typeorm";

export const UserService = {
  yarat: async (data) => {
    const user = UserRepository.create(data);          // obyekt (2.6)
    return UserRepository.save(user);                  // saqlaydi (hook — Misol 2)
  },

  topId: (id) => UserRepository.findOne({ where: { id } }),   // parolsiz (2.4)

  loginUchun: (email) => UserRepository.findForLogin(email),  // parol bilan (Misol 3)

  royxat: (params) => UserRepository.royxat(params),   // (Misol 3)

  yangila: (id, data) => UserRepository.update({ id }, data),

  ochir: (id) => UserRepository.softDelete({ id }),    // soft delete (2.6)
};

Misol 5 — Relations (eager loading — 2.11)

js
// Buyurtma + user + mahsulotlar (relations — 2.8, 2.11)
export const buyurtmaToliq = (id) =>
  AppDataSource.getRepository(Order).findOne({
    where: { id },
    relations: { user: true, items: { product: true } },   // ichma-ich (JOIN)
  });

// Query builder bilan (murakkab — 2.9)
export const userBuyurtmalari = (userId) =>
  AppDataSource.getRepository(Order)
    .createQueryBuilder("o")
    .leftJoinAndSelect("o.user", "u")
    .where("u.id = :userId", { userId })
    .andWhere("o.holat = :holat", { holat: "tugallandi" })
    .orderBy("o.createdAt", "DESC")
    .getMany();

Misol 6 — Tranzaksiya (managed — 2.12)

js
import { AppDataSource } from "../data-source.js";

// Pul o'tkazish — managed transaction (2.12, 6.9)
export async function pulOtkaz(fromId, toId, summa) {
  return AppDataSource.transaction(async (manager) => {   // managed (avtomatik commit/rollback)
    // Atomik decrement (6.9: 2.11)
    await manager.decrement(Account, { id: fromId }, "balans", summa);

    const from = await manager.findOneBy(Account, { id: fromId });
    if (from.balans < 0) throw new Error("Balans yetarli emas");   //  rollback

    await manager.increment(Account, { id: toId }, "balans", summa);
    await manager.save(Transfer, { fromId, toId, summa });
  });
}

Misol 7 — Migration (production — 2.13)

ts
// migrations/1700000000-CreateUsers.ts
import { MigrationInterface, QueryRunner, Table } from "typeorm";

export class CreateUsers1700000000 implements MigrationInterface {
  async up(queryRunner: QueryRunner): Promise<void> {     // qo'llash (2.13)
    await queryRunner.createTable(new Table({
      name: "users",
      columns: [
        { name: "id", type: "int", isPrimary: true, isGenerated: true, generationStrategy: "increment" },
        { name: "ism", type: "varchar", length: "50" },
        { name: "email", type: "varchar", isUnique: true },
      ],
    }));
  }
  async down(queryRunner: QueryRunner): Promise<void> {    // qaytarish (6.14)
    await queryRunner.dropTable("users");
  }
}
// CLI: typeorm migration:generate (entity'dan avtomatik); migration:run; migration:revert

Misol 8 — Express controller (5.6, 5.10)

js
import { UserService } from "../services/userService.js";

export const userlarOl = async (req, res, next) => {
  try {
    const [users, jami] = await UserService.royxat(req.query);   // getManyAndCount (Misol 3)
    res.json({ success: true, data: users, jami });              // (5.7)
  } catch (err) { next(err); }
};

export const userYarat = async (req, res, next) => {
  try {
    const user = await UserService.yarat(req.body);
    const { parol, ...natija } = user;                           // parolsiz (14)
    res.status(201).json({ success: true, data: natija });
  } catch (err) {
    if (err.code === "23505") {                                  // PostgreSQL unique (6.6: Misol 4)
      return res.status(409).json({ error: "Email band" });      // (5.10)
    }
    next(err);
  }
};

5. To'g'ri va noto'g'ri holatlar

1) Production'da synchronize: true

text
 synchronize: true  ma'lumot yo'qolishi (2.13, 14)
 production: synchronize: false + migration

2) reflect-metadata import qilmaslik

js
//  dekoratorlar ishlamaydi (2.3)
import { Entity } from "typeorm";

//  eng tepada
import "reflect-metadata";
import { Entity } from "typeorm";

3) N+1 (relations o'rniga tsikl)

js
//  N+1 (6.10: 2.11)
for (const u of users) u.orders = await orderRepo.find({ where: { user: { id: u.id } } });

//  relations
await userRepo.find({ relations: { orders: true } });

4) DB mantig'i service/controller'da aralash

text
 controller'da to'g'ridan query (toza emas — 9)
 repository pattern (DB mantig'i repository'da — 2.7)

5) Query builder string birlashtirish

js
//  injection (14, 2.9)
.where(`u.email = '${email}'`)

//  parametrli
.where("u.email = :email", { email })

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Cannot read 'getRepository' of undefined / dekorator ishlamaydi

Sababi: reflect-metadata yo'q, yoki tsconfig'da experimentalDecorators/emitDecoratorMetadata yo'q (2.3, 7.6). Yechimi: import qiling; tsconfig sozlang.

Xato 2 — EntityMetadataNotFound

Sababi: entity DataSource'ga qo'shilmagan 2.3-bob. Yechimi: entities: [User, ...] ro'yxatga qo'shing; initialize qiling.

Xato 3 — Production'da ma'lumot yo'qoldi

Sababi: synchronize: true 2.13-bob. Yechimi: false + migration; zaxira.

Xato 4 — Parol qaytdi

Sababi: select: false yo'q, yoki query builder addSelect 2.4-bob. Yechimi: select: false; faqat login'da addSelect.

Xato 5 — N+1 (sekin)

Sababi: lazy/tsikl 2.11-bob. Yechimi: relations (eager); query builder leftJoinAndSelect.

Xato 6 — Relation undefined

Sababi: relations so'ralmagan, yoki ikki tomonlama ta'rif yo'q 2.10-bob. Yechimi: relations: { orders: true }; @OneToMany + @ManyToOne juft.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • SQL (6.4-6.10): TypeORM SQL ustida; query builder/raw.
  • PostgreSQL/MySQL (6.5, 6.6): TypeORM type.
  • Sequelize/Prisma (6.11, 6.12): muqobil ORM'lar.
  • TypeScript (7): class, dekorator 7.6-bob.
  • NestJS 8.3-bob: @nestjs/typeorm, @InjectRepository — tabiiy.
  • Tranzaksiya/ACID 6.9-bob: transaction/QueryRunner.
  • Indeks/N+1 6.10-bob: relations, @Index.
  • Migration 6.14-bob: migration:run.
  • Clean architecture (9): repository pattern.
  • Auth 5.15-bob: User entity, parol hook.

8. Eng yaxshi amaliyotlar (best practices)

  • Production'da synchronize: false + migration (eng muhim — 2.13, 14).
  • Repository pattern (Data Mapper — toza, NestJS — 2.2, 2.7, 9).
  • reflect-metadata eng tepada (dekorator — 2.3).
  • Relations (eager) o'ylab (N+1 oldini oling — 2.11, 6.10).
  • select: false maxfiy maydon (parol; login'da addSelect — 2.4).
  • Query builder/raw parametrli (injection — 2.9, 2.14, 14).
  • Tranzaksiya managed (ko'p yozuv — 2.12).
  • Base entity (umumiy ustunlar — DRY).
  • Custom repository (murakkab so'rov bir joyda — 2.7).
  • Xato kodlari (23505 409 — Misol 8, 5.10).

9. Amaliy loyiha: "TypeORM bilan Backend (repository pattern)"

TypeORM'ni professional darajada mustahkamlash (NestJS'ga tayyorgarlik).

Maqsad

TypeORM bilan repository-pattern asosli, migration-asosli backend qurish: entity, relations, query builder, tranzaksiya.

Talablar (requirements)

  1. DataSource: synchronize: false, migrations, logging (Misol 1, 2.3, 2.13).
  2. Entity'lar: User/Order/Product — dekorator, relations, hook, select:false (Misol 2, 2.4, 2.10).
  3. Custom repository: login (parol bilan), sahifalangan ro'yxat (Misol 3, 2.7).
  4. CRUD service: repository orqali; soft delete (Misol 4, 2.6).
  5. Relations: eager (relations) + query builder (Misol 5, 2.9, 2.11).
  6. Tranzaksiya: pul/buyurtma (managed — Misol 6, 2.12).
  7. Migration: jadval yaratish (up/down — Misol 7, 2.13).
  8. Express: xato kodlari (23505409 — Misol 8).
  9. N+1 oldini olish: relations bilan 2.11-bob.
  10. Repository pattern: DB mantig'i repository'da (toza — 9).

Maslahatlar (hint)

  • reflect-metadata eng tepada (2.3, 2-xato).
  • synchronize: false (production — 2.13, 1-xato).
  • Repository extend (custom — 2.7).
  • Relations: @OneToMany + @ManyToOne juft (2.10, 6-xato).
  • Query builder parametrli (:x — 2.9, 5-xato).
  • select: false + addSelect (login — 2.4, 4-xato).

"Tayyor" mezonlari (acceptance criteria)

  • DataSource (synchronize: false, migration).
  • Entity'lar (dekorator, relations, hook).
  • Custom repository (login, ro'yxat).
  • CRUD service (repository, soft delete).
  • Relations (eager + query builder).
  • Tranzaksiya (managed).
  • Migration (up/down).
  • Xato kodlari (409).
  • N+1 yo'q.
  • Repository pattern (toza).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda uchinchi ORM — TypeORM ni chuqur o'rgandik:

  • TypeORM (dekorator asosida — 2.1); Active Record vs Data Mapper (repository — afzal — 2.2); DataSource 2.3-bob.
  • Entity (@Entity/@Column dekorator — 2.4, 2.5); Repository (CRUD — 2.6); custom repository (toza — 2.7); operatorlar 2.8-bob; Query Builder (murakkab — 2.9).
  • Relations (@OneToMany/@ManyToOne/@JoinColumn/@JoinTable — 2.10); eager/lazy 2.11-bob; tranzaksiya (managed/QueryRunner — 2.12).
  • synchronize: false + migration (production — 2.13); NestJS bilan tabiiy (8.3 — 2.15); Prisma vs TypeORM 2.16-bob.

Keyingi bob — 6.14-bob: Migration va seeding strategiyalari. Uch ORM'ni bildik; har birida migration ko'rdik. Endi migration'ning strategiyalarini — production'da xavfsiz schema o'zgartirish (zero-downtime, expand-contract), rollback, seeding (boshlang'ich/test ma'lumot) — chuqur, ORM-mustaqil o'rganamiz. Bu — real production'da DB'ni boshqarishning asosi.


Foydalanilgan rasmiy/ishonchli manbalar

  • typeorm.io — Entities, Relations, Repository, Query Builder, DataSource, Transactions
  • TypeORM docs — Active Record vs Data Mapper; OneUptime — TypeORM with NestJS 2026
  • Medium (Libeo/Anton) — TypeORM best practices, repository pattern

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
6.13-bob: ORM — TypeORM (entity, repository, relations) — Wisar