8.3-bob: DB ulanish — TypeORM / Prisma / Mongoose bilan
8-QISM — NestJS (chuqur) · 3-mavzu
1. Kirish va motivatsiya
NestJS arxitekturasi 8.1-bob va DI 8.2-bob ni chuqur bildik. Endi NestJS'ni ma'lumotlar bazasiga ulaymiz — har real ilovaning yuragi. 6-QISMda ORM'larni (TypeORM 6.13, Prisma 6.12, Mongoose 6.2) mustaqil o'rgandik; endi ularni NestJS ichida — DI bilan integratsiyalashgan, modul tizimiga mos — ishlatamiz. Bu — karyerangizda eng ko'p ishlatadigan amaliyot (CRUD API, biznes ma'lumot).
NestJS uchchala ORM bilan ham ishlaydi, lekin har birining integratsiya naqshi boshqacha: TypeORM (@nestjs/typeorm — forRoot/forFeature, repository inject — dekorator asosida, NestJS bilan eng tabiiy); Mongoose (@nestjs/mongoose — schema + model inject — MongoDB uchun); Prisma (rasmiy wrapper yo'q — PrismaService orqali DI — eng zamonaviy, type-safe). Tanlov — DB turi va loyihaga bog'liq: SQL + enterprise TypeORM; MongoDB Mongoose; zamonaviy/type-safe Prisma.
Asosiy g'oya: ORM'ni NestJS modul sifatida ulaysiz (forRoot — global ulanish), keyin har feature modulda repository/model/service ni inject qilasiz (DI — 8.2). Bu — 6-QISMdagi ORM bilimini NestJS DI + modul tizimi bilan birlashtiradi. Bu bob: uchchala ORM integratsiyasi (asosiy e'tibor — TypeORM va Prisma), entity/schema, repository inject, va real CRUD — chuqur.
O'xshatish: ORM — DB bilan gaplashadigan tarjimon (6-QISM). NestJS bilan integratsiya — bu tarjimonni kompaniyaga rasmiy xodim sifatida ishga olish (DI konteyner — 8.2):
forRoot— tarjimonni yollash (ulanish);forFeature/@InjectRepository— har bo'limga (modul) tarjimonni biriktirish. Endi har service tarjimonni so'raydi (DI), kompaniya (NestJS) beradi. Tashkillangan, qayta ishlatiladigan.
Nega muhim?
- Har ilova yuragi — DB'siz ilova yo'q (ma'lumot saqlash).
- NestJS + ORM — eng ko'p ishlatadigan amaliyot (CRUD).
- DI integratsiya — repository/model inject (toza, testlanadigan).
- Tanlov — TypeORM/Prisma/Mongoose — loyihaga mos.
2. Nazariya — chuqur tushuntirish
2.1. NestJS DB integratsiyasi (umumiy g'oya)
Uch qadam (har ORM uchun):
1. forRoot (yoki PrismaService) — GLOBAL ulanish (bir marta — app.module)
2. Entity/Schema — DB model (6.13, 6.2)
3. forFeature + inject — har feature modulda repository/model (DI — 8.2)
ORM modul sifatida (8.1: 2.13 dynamic module — forRoot)
repository/model — DI orqali service'ga (8.2)NestJS DB naqshi: ORM — dynamic module (
forRoot— sozlanadigan ulanish — 8.1: 2.13). Ulanish bir marta (app.module), keyin har feature modul kerakli model/repository'ni oladi (forFeature+ inject — DI — 8.2). Bu — 6-QISM ORM + 8-QISM DI birlashmasi.
2.2. ORM tanlovi (qaysi qachon — NestJS kontekstida)
┌──────────┬─────────────────────────┬──────────────────────┐
│ ORM │ Qachon │ NestJS integratsiya │
├──────────┼─────────────────────────┼──────────────────────┤
│ TypeORM │ SQL + enterprise/dekorator│ @nestjs/typeorm (tabiiy)│
│ Prisma │ zamonaviy, type-safe │ PrismaService (wrappersiz)│
│ Mongoose │ MongoDB │ @nestjs/mongoose │
└──────────┴─────────────────────────┴──────────────────────┘Tanlov (2026): TypeORM — SQL + dekorator (NestJS bilan eng tabiiy — entity dekoratorlari NestJS uslubiga mos). Prisma — zamonaviy, type-safe, tez o'syapti (2025'da Mongoose'ni o'tib ketdi). Mongoose — MongoDB (TypeORM MongoDB'da kuchsiz). Loyihaga qarab; bu bobda asosan TypeORM + Prisma.
Batafsil taqqoslash (qaysi mezon bo'yicha qaysi):
┌──────────────────┬─────────────┬──────────────┬─────────────┐
│ Mezon │ TypeORM │ Prisma │ Mongoose │
├──────────────────┼─────────────┼──────────────┼─────────────┤
│ DB turi │ SQL (+Mongo │ SQL (Mongo │ MongoDB │
│ │ kuchsiz) │ preview) │ (faqat) │
│ Model ta'rifi │ Entity │ schema.prisma│ @Schema │
│ │ (dekorator) │ (DSL fayl) │ (dekorator) │
│ Type-safety │ Yaxshi │ Eng kuchli │ O'rtacha │
│ │ │ (generatsiya)│ │
│ NestJS paket │ @nestjs/ │ yo'q │ @nestjs/ │
│ │ typeorm │ (PrismaSvc) │ mongoose │
│ Migration │ CLI+kod │ prisma │ qo'lda/ │
│ │ │ migrate │ skript │
│ Query builder │ Bor (kuchli)│ yo'q (fluent │ chaining │
│ │ │ API) │ │
│ O'rganish │ O'rtacha │ Oson │ Oson │
│ Qachon tanla │ Enterprise, │ Yangi loyiha,│ MongoDB │
│ │ murakkab SQL│ DX, type-safe│ hujjatli │
└──────────────────┴─────────────┴──────────────┴─────────────┘Amaliy qoida: SQL DB + murakkab so'rovlar / mavjud enterprise kod TypeORM; yangi loyiha, ishlab chiquvchi qulayligi (DX) va maksimal type-safety muhim Prisma; ma'lumot modeli hujjatli (document) va MongoDB Mongoose. Uchchalasi ham production'ga yaroqli — "eng yaxshisi" yo'q, faqat loyihaga mosi bor.
2.3. TypeORM: forRoot (global ulanish)
TypeOrmModule.forRoot — DB'ga global ulanish (app.module — docs):
// app.module.ts
import { TypeOrmModule } from "@nestjs/typeorm";
@Module({
imports: [
TypeOrmModule.forRoot({
type: "postgres", // DB turi (6.6)
url: process.env.DATABASE_URL, // (8.14: ConfigService bilan — 2.4)
entities: [User, Order], // yoki autoLoadEntities: true
synchronize: false, // production'da FALSE! (6.13: 2.13)
migrations: ["dist/migrations/*.js"],
}),
],
})
export class AppModule {}
forRoot— bir marta (app.module), butun ilovaga DB ulanish (8.1: 2.13 dynamic module).synchronize: false(production — 6.13: 2.13).autoLoadEntities: true— entity'larni avtomatik (qo'lda ro'yxat o'rniga). Async sozlash —forRootAsync(ConfigService bilan — 2.4).
2.4. forRootAsync (ConfigService bilan)
// Env'dan sozlash (8.14 — ConfigService DI)
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService], // DI (8.2)
useFactory: (config: ConfigService) => ({ // useFactory (8.2: 2.11)
type: "postgres",
url: config.get("DATABASE_URL"), // env (8.14)
synchronize: config.get("NODE_ENV") !== "production",
autoLoadEntities: true,
}),
}),
forRootAsync— sozlamani dinamik (ConfigService'dan — env — 8.14).useFactory(8.2: 2.11) +inject(ConfigService). Bu — to'g'ri amaliyot (kalitlar .env'da — 5.8, 14, hardcode emas).
2.4a. Connection pooling (ulanishlar hovuzi)
DB ulanishini ochish — qimmat (TCP + autentifikatsiya). Shuning uchun har ORM pool (ulanishlar hovuzi) ishlatadi: bir necha tayyor ulanishni ochib qo'yadi, so'rov kelganda pool'dan oladi, tugagach qaytaradi (6.5, 6.9). Bu — DB integratsiyasining eng ko'p e'tibordan chetda qoladigan, lekin production'da eng muhim sozlamasi.
// TypeORM — pool sozlash (postgres/mysql driver'ining "extra" orqali)
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: "postgres",
url: config.get("DATABASE_URL"),
autoLoadEntities: true,
extra: {
max: 10, // maksimal ulanish soni (pool hajmi)
idleTimeoutMillis: 30000, // bo'sh ulanish 30s'dan keyin yopiladi
connectionTimeoutMillis: 5000, // ulanish ololmasa 5s'da xato
},
}),
}), Prisma — pool DATABASE_URL query-param orqali:
postgresql://user:parol@host:5432/db?connection_limit=10&pool_timeout=20
Mongoose — forRoot options orqali:
MongooseModule.forRoot(url, { maxPoolSize: 10, minPoolSize: 2 })Pool hajmi (
max/connection_limit) — muhim sozlama. Juda kichik so'rovlar navbatda kutadi (sekin). Juda katta DB'ni bosadi (Postgres defaultmax_connections~100; har ilova instansiyasi × pool hajmi ≤ shu chegara). Serverless (Lambda/Vercel) da har instansiya alohida pool ochadi — u yerda PgBouncer yoki Prisma Accelerate/Data Proxy ishlatiladi (ko'p instansiya bitta pool'ni bo'lishadi). Qoida:pool_hajmi × instansiya_soni < DB_max_connections6.5-bob. Bir instansiya uchun 10–20 odatda yetadi.
2.5. TypeORM: Entity (6.13 takrori)
// users/entities/user.entity.ts (6.13: 2.4)
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
@Entity("users")
export class User {
@PrimaryGeneratedColumn() id: number;
@Column({ unique: true }) email: string;
@Column({ select: false }) parol: string; // (5.15, 14)
@OneToMany(() => Order, (o) => o.user) orders: Order[];
}Entity — 6.13'dagi kabi (dekorator). NestJS'da entity'lar
forFeatureda ro'yxatga olinadi 2.6-bob. NestJS dekorator (8.1: 7.6) bilan tabiiy uyg'unlik (ikkalasi dekorator).
2.6. TypeORM: forFeature + InjectRepository
forFeature — feature modulda entity'larni ro'yxatga oladi; @InjectRepository — repository inject:
// users.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])], // bu modulda User entity
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
// users.service.ts
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private userRepo: Repository<User>, // DI (8.2)
) {}
hammasi() { return this.userRepo.find(); } // TypeORM repository (6.13: 2.6)
}
forFeature+@InjectRepository— TypeORM NestJS naqshi:forFeature([User])(modulda entity),@InjectRepository(User)(service'ga repository — DI — 8.2).Repository<User>— TypeORM repository (6.13: find/save/...). Bu — eng ko'p ishlatiladigan TypeORM+NestJS naqsh.
2.7. TypeORM: CRUD service (repository bilan)
@Injectable()
export class UsersService {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
async hammasi(): Promise<User[]> {
return this.repo.find(); // (6.13: 2.6)
}
async bitta(id: number): Promise<User> {
const user = await this.repo.findOne({ where: { id } });
if (!user) throw new NotFoundException("Topilmadi"); // (8.1: 2.6 — 404)
return user;
}
async yarat(dto: CreateUserDto): Promise<User> {
const user = this.repo.create(dto); // obyekt (6.13)
return this.repo.save(user); // saqlash
}
async ochir(id: number): Promise<void> {
await this.repo.delete(id);
}
}Repository metodlari — 6.13'dagi kabi (
find/findOne/create/save/delete). NestJS qo'shadi: DI (repository inject), built-in exception (NotFoundException — 8.1). Toza qatlam (controller service repository — 8.1: 2.9).
2.7a. DataSource inject va raw SQL
Repository yetmaganda (murakkab query, raw SQL, migration'siz DDL) — DataSource to'g'ridan inject qilinadi. DataSource — TypeORM'ning markaziy ulanish obyekti (6.13: 2.2), NestJS uni DI'ga qo'yadi:
import { DataSource } from "typeorm";
@Injectable()
export class HisobotService {
constructor(private dataSource: DataSource) {} // DI — provider avtomatik
// Raw SQL (murakkab hisobot; parametrli — SQL injection'dan himoya — 14)
async oylikDaromad(yil: number) {
return this.dataSource.query(
`SELECT date_trunc('month', created_at) AS oy, SUM(summa) AS jami
FROM orders WHERE EXTRACT(year FROM created_at) = $1
GROUP BY oy ORDER BY oy`,
[yil], // parametr ($1) — hech qachon string konkatenatsiya emas (14)
);
}
// Repository'ni DataSource orqali olish (dinamik)
ixtiyoriyRepo() {
return this.dataSource.getRepository(User);
}
}
DataSource— TypeORM ulanishi (6.13: 2.2), NestJS DI'ga tayyor qo'yadi (forRootdan keyin). Ishlatiladi: rawquery()(parametrli —$1/?— SQL injection'dan himoya, 14), tranzaksiya 2.13-bob,getRepository(). Repository yetsa — raw'ga o'tmang (repository/query builder xavfsizroq va typed). Raw — faqat repository imkoni yetmaganda.
2.8. Prisma: PrismaService (wrappersiz)
Prisma'da rasmiy NestJS wrapper YO'Q — PrismaService qo'lda yaratiladi (docs/prisma):
// prisma/prisma.service.ts
import { Injectable, OnModuleInit } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() { // lifecycle (8.1: 2.15)
await this.$connect(); // DB ulanish
}
}
// PrismaClient'ni extend qiladi 6.12-bob — barcha Prisma metodlari + DI// prisma/prisma.module.ts (global — 8.1: 2.13)
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}Prisma — wrappersiz (rasmiy Prisma tavsiyasi):
PrismaService extends PrismaClient6.12-bob +OnModuleInit(ulanish — 8.1: 2.15). Global modul (har joyda). Boshqa ORM'lardan farqi: NestJS-maxsus paket yo'q, to'g'ridan Prisma Client + DI. Eng sodda, type-safe 6.12-bob.
Nega extends PrismaClient? PrismaService — PrismaClient'ning o'zi (meros orqali), shuning uchun this.user, this.$transaction, this.$queryRaw — barcha Prisma Client metodlari to'g'ridan mavjud. Ustiga NestJS lifecycle (OnModuleInit) qo'shiladi: modul ishga tushganda $connect. Bu — kompozitsiya emas, meros: bitta obyekt ham Prisma Client, ham NestJS provider (DI'ga tushadigan).
2.8a. Prisma: enableShutdownHooks (graceful shutdown)
Prisma 5+ da $connect/$disconnect uchun OnModuleInit/OnModuleDestroy yetarli (tavsiya etilgan usul). Lekin NestJS'ning enableShutdownHooks signalini (SIGINT/SIGTERM) Prisma'ga bog'lash kerak bo'lsa — main.tsda yoqiladi:
// main.ts — graceful shutdown (5.10)
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks(); // NestJS: SIGTERM/SIGINT'da onModuleDestroy chaqiriladi
await app.listen(3000);
}// prisma.service.ts — OnModuleDestroy Prisma ulanishini yopadi
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() { await this.$connect(); }
async onModuleDestroy() { await this.$disconnect(); } // shutdown'da (5.10)
}Graceful shutdown 5.10-bob:
app.enableShutdownHooks()— NestJS SIGTERM/SIGINT'ni tinglaydi vaonModuleDestroychaqiradi; u yerda$disconnectDB ulanishini toza yopadi (ochiq ulanishlar "leak" bo'lmaydi). Docker/K8s'da konteyner to'xtaganda muhim 5.10-bob. Eski Prisma (4.x) daprocess.on('beforeExit')ishlatilardi — Prisma 5+ da bunga hojat yo'q,onModuleDestroyyetarli.
2.9. Prisma: service'da ishlatish
// users.service.ts
import { PrismaService } from "../prisma/prisma.service";
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {} // DI (8.2)
hammasi() {
return this.prisma.user.findMany(); // Prisma Client (6.12: 2.9)
}
async bitta(id: number) {
const user = await this.prisma.user.findUnique({ where: { id } });
if (!user) throw new NotFoundException("Topilmadi");
return user;
}
yarat(dto: CreateUserDto) {
return this.prisma.user.create({ data: dto }); // (6.12)
}
}Prisma service'da —
this.prisma.user.findMany()(6.12: type-safe). DI orqali PrismaService inject. Eng toza, type-safe (Prisma turlari — 6.12: 2.2). NestJS + Prisma — zamonaviy yetakchi kombinatsiya.
2.10. Mongoose: forRoot + schema
Mongoose — MongoDB uchun (@nestjs/mongoose — docs):
// app.module.ts
import { MongooseModule } from "@nestjs/mongoose";
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGO_URL), // global ulanish (6.2: 2.3)
],
})
export class AppModule {}// users/schemas/user.schema.ts (NestJS dekorator uslubi)
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Document } from "mongoose";
@Schema({ timestamps: true }) // (6.2)
export class User {
@Prop({ required: true }) ism: string;
@Prop({ required: true, unique: true }) email: string;
}
export type UserDocument = User & Document;
export const UserSchema = SchemaFactory.createForClass(User);Mongoose NestJS — schema dekorator bilan (
@Schema,@Prop— 6.2 schema'ning NestJS uslubi).SchemaFactory.createForClass— schema yaratadi.forRoot— ulanish (6.2: 2.3). NestJS dekoratorga moslab 8.1-bob.
2.11. Mongoose: forFeature + InjectModel
// users.module.ts
@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
providers: [UsersService],
})
export class UsersModule {}
// users.service.ts
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
@Injectable()
export class UsersService {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
hammasi() { return this.userModel.find(); } // Mongoose model (6.2: 2.6)
yarat(dto: CreateUserDto) { return this.userModel.create(dto); }
}
forFeature+@InjectModel— Mongoose NestJS naqshi (TypeORM@InjectRepositoryga o'xshash — 2.6).Model<UserDocument>— Mongoose model (6.2: find/create/...). MongoDB loyihalarida shu naqsh.
2.12. Repository pattern (toza arxitektura — 9)
Oddiy: service to'g'ridan repository/prisma ishlatadi (yetarli — kichik/o'rta)
Toza (9: DIP — 8.2: 2.18): service interface'ga bog'liq, ORM yashirin
Service IUserRepository (interface) TypeOrmUserRepository (impl)
ORM'ni almashtirish oson (TypeORM Prisma — service o'zgarmaydi)Repository pattern (8.2: Misol 9, 9): katta loyihada service'ni ORM'dan ajratish (interface + DI). ORM almashtirilsa (TypeORM Prisma), service o'zgarmaydi. Lekin — ko'p loyihada to'g'ridan repository/prisma yetadi (ortiqcha abstraksiya emas). Loyiha hajmiga qarab.
2.13. Tranzaksiya (NestJS'da)
// TypeORM — DataSource inject (6.13: 2.12)
@Injectable()
export class OrderService {
constructor(private dataSource: DataSource) {} // DI
async yarat(dto) {
return this.dataSource.transaction(async (manager) => { // managed (6.13)
const order = await manager.save(Order, dto);
await manager.decrement(Product, { id: dto.productId }, "zaxira", 1);
return order;
});
}
}
// Prisma — $transaction (6.12: 2.14)
async yarat(dto) {
return this.prisma.$transaction(async (tx) => {
const order = await tx.order.create({ data: dto });
await tx.product.update({ where: { id: dto.productId }, data: { zaxira: { decrement: 1 } } });
return order;
});
}Tranzaksiya — 6-QISMdagi kabi (6.13: 2.12 TypeORM DataSource, 6.12: 2.14 Prisma $transaction), lekin DI orqali (DataSource/PrismaService inject). ACID 6.9-bob — NestJS'da ham. Pul, ko'p yozuv uchun.
TypeORM: dataSource.transaction vs QueryRunner (qo'lda boshqarish). Yuqoridagi dataSource.transaction(cb) — managed (avtomatik begin/commit/rollback). Ko'proq nazorat kerak bo'lsa (masalan, tranzaksiya o'rtasida shart bo'yicha savepoint, yoki bir necha repository'ni bitta ulanishga bog'lash) — QueryRunner qo'lda ishlatiladi:
// TypeORM — QueryRunner (qo'lda: begin/commit/rollback — 6.13: 2.12)
@Injectable()
export class OrderService {
constructor(private dataSource: DataSource) {}
async yaratQoldaTx(dto: CreateOrderDto) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect(); // pool'dan ulanish oladi
await queryRunner.startTransaction(); // BEGIN
try {
const order = await queryRunner.manager.save(Order, dto);
await queryRunner.manager.decrement(
Product, { id: dto.productId }, "zaxira", 1,
);
await queryRunner.commitTransaction(); // COMMIT
return order;
} catch (err) {
await queryRunner.rollbackTransaction(); // ROLLBACK (xato bo'lsa)
throw err;
} finally {
await queryRunner.release(); // MUHIM: ulanishni pool'ga qaytar
}
}
}
QueryRunner— qo'lda tranzaksiya:connectstartTransaction(ishlar)commitTransaction/rollbackTransactionrelease(finallyda — aks holda ulanish pool'da "band" qolib, oxir-oqibat pool tugaydi — pool leak).queryRunner.manager— shu tranzaksiyaga bog'langan EntityManager (barcha yozuvlar bitta ulanish/tranzaksiyada). Boshqaruv kerak bo'lmasa —dataSource.transaction(cb)soddaroq (release avtomatik). Prisma'da bunga o'xshash "interaktiv tranzaksiya" —$transaction(async (tx) => ...)(rollback avtomatik,throwbo'lsa).
Prisma: ketma-ket (batch) tranzaksiya. Bir-biriga bog'liq bo'lmagan bir necha operatsiyani atomar bajarish uchun massiv shakli ham bor:
// Prisma — massiv shakli (barcha yoki hech biri — 6.12: 2.14)
await this.prisma.$transaction([
this.prisma.user.create({ data: userDto }),
this.prisma.profil.create({ data: profilDto }),
]);Prisma ikki xil tranzaksiya beradi: interaktiv (
$transaction(async (tx) => {...})— o'rtada mantiq/shart bo'lsa, callback ichidatxishlatiladi) va batch ($transaction([...])— bog'liq bo'lmagan operatsiyalar ro'yxati, hammasi yoki hech biri). Interaktiv — kuchliroq (Misol 5), lekin biroz sekinroq (ulanish tranzaksiya davomida band).
2.14. Migration (NestJS'da)
TypeORM: DataSource config + CLI (6.13: 2.13, 6.14)
- npm run typeorm migration:generate / migration:run
- synchronize: false (production — 6.13: 2.13)
Prisma: prisma migrate (6.12: 2.7, 6.14)
- npx prisma migrate dev / deploy
Migration — 6.14 strategiyalari (expand-contract, rollback)Migration — 6.14 (strategiyalar) NestJS'da ham amal qiladi. TypeORM CLI / Prisma migrate. Production'da
synchronize: false+ migration (6.13: 2.13). CI/CD'damigrate deploy(6.14: 2.14).
TypeORM CLI NestJS'da — alohida DataSource fayli. TypeORM CLI (migration:generate/run) NestJS DI'ni bilmaydi, shuning uchun CLI uchun alohida DataSource eksport qilingan fayl kerak (forRoot sozlamasidan mustaqil):
// typeorm.config.ts — CLI uchun (NestJS'dan tashqarida ishlaydi)
import { DataSource } from "typeorm";
import "dotenv/config"; // .env'ni yuklaydi (8.14)
export default new DataSource({
type: "postgres",
url: process.env.DATABASE_URL,
entities: ["dist/**/*.entity.js"], // build'dan keyingi entity'lar
migrations: ["dist/migrations/*.js"],
});// package.json — skriptlar (typeorm-ts-node yoki build'dan keyin)
{
"scripts": {
"typeorm": "typeorm-ts-node-commonjs -d typeorm.config.ts",
"migration:generate": "npm run typeorm -- migration:generate ./src/migrations/Auto",
"migration:run": "npm run typeorm -- migration:run",
"migration:revert": "npm run typeorm -- migration:revert"
}
}TypeORM migration NestJS'da: CLI DI'siz ishlagani uchun
forRootdagi sozlama CLI'ga ko'rinmaydi — shuning uchuntypeorm.config.ts(DataSourceeksporti) alohida yoziladi va-dbayrog'i bilan beriladi.migration:generate— entity va DB farqidan migration yaratadi 6.14-bob;migration:run— qo'llaydi;migration:revert— orqaga qaytaradi (rollback — 6.14). Production'da:synchronize: false2.3-bob + deploy vaqtidamigration:run(CI/CD — 6.14: 2.14). Prisma'da bu soddaroq:schema.prismayagona manba,prisma migrate dev(dev) /prisma migrate deploy(prod) — alohida config kerak emas.
2.15. Testda DB (mock yoki test DB)
// DI tufayli — test'da repository mock (8.2: 2.17, 8.11)
const moduleRef = await Test.createTestingModule({
providers: [
UsersService,
{ provide: getRepositoryToken(User), useValue: mockRepo }, // TypeORM mock
],
}).compile();Test'da 8.11-bob: repository/PrismaService mock (DI — 8.2: 2.17). TypeORM —
getRepositoryToken(User); Prisma — PrismaService mock. Yoki test DB (e2e — 8.11). DI — DB'ni test uchun almashtirishni oson qiladi.
2.16. Best practices (DB + NestJS — 14)
forRootAsync + ConfigService (kalitlar .env — 8.14, 5.8, 14)
synchronize: false production + migration (6.13: 2.13, 6.14)
forFeature + inject (repository/model — DI — 2.6, 2.11)
Service'da DB; controller yupqa (8.1: 2.7)
Built-in exception (NotFoundException — 8.1)
Tranzaksiya (DataSource/$transaction — 2.13)
Maxfiy maydon (parol select: false — 5.15, 14)
Connection pool sozlash (max/connection_limit — 2.4a)
Graceful shutdown (enableShutdownHooks + $disconnect — 2.8a, 5.10)
Raw SQL parametrli ($1/? — SQL injection'dan himoya — 2.7a, 14)
ORM tanlovi loyihaga (SQLTypeORM, MongoMongoose, zamonaviyPrisma — 2.2)3. Sintaksis — tez ma'lumotnoma
// TypeORM (2.3, 2.6)
TypeOrmModule.forRoot({ type: "postgres", url, entities, synchronize: false })
TypeOrmModule.forFeature([User]) // modulda
@InjectRepository(User) private repo: Repository<User> // inject
// TypeORM raw / tranzaksiya (2.7a, 2.13)
constructor(private dataSource: DataSource) {}
this.dataSource.query("SELECT ... WHERE x = $1", [x]) // parametrli (14)
const qr = this.dataSource.createQueryRunner(); // connect/startTransaction/commit/release
// Prisma (2.8, 2.9)
class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {}
constructor(private prisma: PrismaService) {}
this.prisma.user.findMany()
this.prisma.$transaction(async (tx) => { ... }) // interaktiv (2.13)
// Mongoose (2.10, 2.11)
MongooseModule.forRoot(url) / forFeature([{ name, schema }])
@InjectModel(User.name) private model: Model<UserDocument>4. Batafsil kod namunalari
Misol 1 — TypeORM to'liq sozlash (forRootAsync — 2.4)
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config"; // (8.14)
import { TypeOrmModule } from "@nestjs/typeorm";
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }), // (8.14)
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({ // env'dan (2.4)
type: "postgres",
url: config.get<string>("DATABASE_URL"),
autoLoadEntities: true, // entity'lar avtomatik
synchronize: config.get("NODE_ENV") !== "production", // dev only (6.13: 2.13)
logging: config.get("NODE_ENV") === "development",
}),
}),
UsersModule,
],
})
export class AppModule {}Misol 2 — TypeORM feature modul + service (2.6, 2.7)
// users/users.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
@Module({
imports: [TypeOrmModule.forFeature([User])], // bu modulda User (2.6)
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}// users/users.service.ts
import { Injectable, NotFoundException, ConflictException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./entities/user.entity";
@Injectable()
export class UsersService {
constructor(@InjectRepository(User) private repo: Repository<User>) {} // DI (8.2)
async hammasi(page = 1, limit = 20): Promise<{ data: User[]; jami: number }> {
const [data, jami] = await this.repo.findAndCount({ // (6.13)
skip: (page - 1) * limit, take: limit, order: { id: "DESC" },
});
return { data, jami };
}
async bitta(id: number): Promise<User> {
const user = await this.repo.findOne({ where: { id } });
if (!user) throw new NotFoundException(`${id}-foydalanuvchi topilmadi`); // (8.1)
return user;
}
async yarat(dto: CreateUserDto): Promise<User> {
const mavjud = await this.repo.findOne({ where: { email: dto.email } });
if (mavjud) throw new ConflictException("Email band"); // 409 (8.1)
const user = this.repo.create(dto); // (6.13)
return this.repo.save(user);
}
async yangila(id: number, dto: UpdateUserDto): Promise<User> {
const user = await this.bitta(id); // 404 (qayta ishlatish)
Object.assign(user, dto);
return this.repo.save(user);
}
async ochir(id: number): Promise<void> {
const r = await this.repo.delete(id);
if (r.affected === 0) throw new NotFoundException("Topilmadi");
}
}Misol 3 — TypeORM relations + query builder (6.13, 6.7)
@Injectable()
export class OrdersService {
constructor(@InjectRepository(Order) private repo: Repository<Order>) {}
// Relations (eager — 6.13: 2.11)
async toliq(id: number) {
return this.repo.findOne({
where: { id },
relations: { user: true, items: { product: true } }, // JOIN (6.13)
});
}
// Query builder (murakkab — 6.13: 2.9)
async statistika(userId: number) {
return this.repo
.createQueryBuilder("o")
.select("COUNT(o.id)", "soni")
.addSelect("SUM(o.summa)", "jami")
.where("o.userId = :userId", { userId }) // parametrli (14)
.andWhere("o.holat = :holat", { holat: "tugallandi" })
.getRawOne();
}
}Misol 4 — Prisma to'liq (service, module — 2.8, 2.9)
// prisma/prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() { await this.$connect(); } // ulanish (8.1: 2.15)
async onModuleDestroy() { await this.$disconnect(); } // graceful (5.10)
}// prisma/prisma.module.ts (global — 8.1: 2.13)
import { Global, Module } from "@nestjs/common";
import { PrismaService } from "./prisma.service";
@Global()
@Module({ providers: [PrismaService], exports: [PrismaService] })
export class PrismaModule {}// users/users.service.ts (Prisma)
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {} // DI
hammasi(page = 1, limit = 20) {
return this.prisma.user.findMany({ // (6.12: 2.9)
skip: (page - 1) * limit, take: limit,
select: { id: true, ism: true, email: true }, // parolsiz (14)
orderBy: { createdAt: "desc" },
});
}
async bitta(id: number) {
const user = await this.prisma.user.findUnique({ where: { id } });
if (!user) throw new NotFoundException("Topilmadi");
return user;
}
yarat(dto: CreateUserDto) {
return this.prisma.user.create({ data: dto });
}
}Misol 5 — Prisma relations + tranzaksiya (6.12)
@Injectable()
export class OrdersService {
constructor(private prisma: PrismaService) {}
// include (relations — 6.12: 2.12)
toliq(id: number) {
return this.prisma.order.findUnique({
where: { id },
include: { user: true, items: { include: { product: true } } }, // nested
});
}
// Tranzaksiya (6.12: 2.14)
async yarat(userId: number, productId: number) {
return this.prisma.$transaction(async (tx) => {
const product = await tx.product.findUnique({ where: { id: productId } });
if (!product || product.zaxira < 1) throw new BadRequestException("Zaxira yo'q");
await tx.product.update({ where: { id: productId }, data: { zaxira: { decrement: 1 } } });
return tx.order.create({ data: { userId, summa: product.narx } });
});
}
}Misol 6 — Mongoose to'liq (schema, service — 2.10, 2.11)
// users/schemas/user.schema.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { HydratedDocument } from "mongoose";
@Schema({ timestamps: true })
export class User {
@Prop({ required: true }) ism: string;
@Prop({ required: true, unique: true, lowercase: true }) email: string;
@Prop({ required: true, select: false }) parol: string; // (5.15, 14)
@Prop({ type: String, enum: ["user", "admin"], default: "user" }) rol: string;
}
export type UserDocument = HydratedDocument<User>;
export const UserSchema = SchemaFactory.createForClass(User);// users/users.service.ts (Mongoose)
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { User, UserDocument } from "./schemas/user.schema";
@Injectable()
export class UsersService {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
hammasi() { return this.userModel.find().select("-parol").lean(); } // (6.2)
async bitta(id: string) {
const user = await this.userModel.findById(id);
if (!user) throw new NotFoundException("Topilmadi");
return user;
}
yarat(dto: CreateUserDto) { return this.userModel.create(dto); }
}Misol 7 — Controller (ORM-agnostik — 8.1: 2.7)
// users/users.controller.ts — ORM'dan mustaqil (controller yupqa)
@Controller("users")
export class UsersController {
constructor(private usersService: UsersService) {} // service'ga bog'liq (ORM emas)
@Get()
hammasi(@Query("page") page = "1") {
return this.usersService.hammasi(+page);
}
@Get(":id")
bitta(@Param("id") id: string) {
return this.usersService.bitta(+id);
}
@Post()
yarat(@Body() dto: CreateUserDto) {
return this.usersService.yarat(dto);
}
}
// Controller ORM'ni bilmaydi (service yashiradi) — TypeORM/Prisma/Mongoose almashtirsa o'zgarmaydiMisol 8 — Repository pattern (ORM abstraksiya — 2.12, 9)
// Interface (9: DIP — 8.2: 2.18)
export interface IUserRepository {
hammasi(): Promise<User[]>;
topId(id: number): Promise<User | null>;
saqla(user: Partial<User>): Promise<User>;
}
export const USER_REPOSITORY = Symbol("USER_REPOSITORY");
// TypeORM implementatsiya
@Injectable()
export class TypeOrmUserRepository implements IUserRepository {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
hammasi() { return this.repo.find(); }
topId(id: number) { return this.repo.findOne({ where: { id } }); }
saqla(user: Partial<User>) { return this.repo.save(this.repo.create(user)); }
}
// Modul (almashtirsa bo'ladi — 8.2: 2.8)
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [
UsersService,
{ provide: USER_REPOSITORY, useClass: TypeOrmUserRepository }, // ORM yashirin
],
})
export class UsersModule {}
// Service interface'ga bog'liq (ORM bilmaydi — 9)
@Injectable()
export class UsersService {
constructor(@Inject(USER_REPOSITORY) private repo: IUserRepository) {}
}
// Prisma'ga o'tilsa — PrismaUserRepository yozib, useClass almashtiriladi (service o'zgarmaydi)5. To'g'ri va noto'g'ri holatlar
1) synchronize: true production'da
ma'lumot yo'qolishi (6.13: 2.13)
false + migration (6.14)2) Kalitlarni hardcode
// kodda (14, 5.8)
forRoot({ url: "postgres://admin:parol@..." })
// forRootAsync + ConfigService
forRootAsync({ useFactory: (c) => ({ url: c.get("DATABASE_URL") }) })3) forFeature unutish
entity forFeature'da yo'q "Cannot resolve repository" (2.6)
TypeOrmModule.forFeature([User]) modulda4) Controller'da to'g'ridan repository
// controller DB'ni biladi (qatlam buzildi — 8.1: 2.7)
@InjectRepository(User) repo // controller'da
// service orqali
constructor(private service: UsersService) {}5) Parolni qaytarish
parol JSON'da (14, 5.15)
select: false (entity/schema); select bilan kerakli maydon6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Cannot resolve dependencies (Repository)
Sababi: forFeature'da entity yo'q 2.6-bob. Yechimi: TypeOrmModule.forFeature([User]) modulda.
Xato 2 — EntityMetadataNotFound
Sababi: entity forRoot'da ro'yxatda yo'q 2.3-bob. Yechimi: entities: [...] yoki autoLoadEntities: true.
Xato 3 — Prisma PrismaClient is not connected
Sababi: $connect chaqirilmagan 2.8-bob. Yechimi: OnModuleInit'da $connect.
Xato 4 — Cannot read ... of undefined (model)
Sababi: Mongoose forFeature/InjectModel nomi mos emas 2.11-bob. Yechimi: User.name izchil.
Xato 5 — Connection xatosi
Sababi: DATABASE_URL noto'g'ri, DB ishlamayapti (6.5, 6.6). Yechimi: .env tekshir; DB ishga tushir.
Xato 6 — Migration production'da xato
Sababi: synchronize: true yoki migration noto'g'ri 6.14-bob. Yechimi: false + migrate deploy (6.14: 2.14).
Xato 7 — remaining connection slots are reserved / pool tugadi
Sababi: pool juda katta yoki QueryRunner.release() chaqirilmagan (ulanish leak — 2.13), yoki ko'p instansiya × pool > DB max_connections (2.4a). Yechimi: release() ni finallyda; pool hajmini kamaytir; serverless'da PgBouncer/Accelerate (2.4a).
Xato 8 — Konteyner to'xtaganda ulanish "osilib" qoldi
Sababi: enableShutdownHooks yoqilmagan yoki onModuleDestroyda $disconnect/pool yopilmagan (2.8a). Yechimi: app.enableShutdownHooks() + onModuleDestroy (Prisma $disconnect) — 2.8a, 5.10.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- ORM'lar (6.2, 6.12, 6.13): NestJS'da DI bilan.
- DI 8.2-bob: repository/model/PrismaService inject.
- Dynamic module (8.1: 2.13): forRoot/forFeature.
- Config 8.14-bob: forRootAsync + ConfigService.
- Tranzaksiya/ACID 6.9-bob: DataSource/$transaction.
- Migration 6.14-bob: strategiyalar.
- Relations 8.4-bob: keyingi bob.
- DTO/validatsiya 8.5-bob: create/update.
- Testing 8.11-bob: repository mock.
- Clean architecture (9): repository pattern.
8. Eng yaxshi amaliyotlar (best practices)
- forRootAsync + ConfigService (kalitlar .env — 2.4, 14).
- synchronize: false production + migration (6.13: 2.13, 6.14).
- forFeature + inject (repository/model — DI — 2.6, 2.11).
- Service'da DB; controller yupqa (qatlam — 8.1: 2.7, Misol 7).
- Built-in exception (NotFound/Conflict — 8.1).
- Tranzaksiya muhim amalda (DataSource/$transaction — 2.13).
- Maxfiy maydon (parol select: false — 5.15, 14).
- Connection pool sozlash (
max/connection_limit; serverless'da PgBouncer — 2.4a). - Graceful shutdown (
enableShutdownHooks+onModuleDestroy$disconnect— 2.8a, 5.10). - QueryRunner'da
releasehar doimfinallyda (pool leak'dan saqlanish — 2.13). - ORM tanlovi loyihaga (SQL/Mongo/zamonaviy — 2.2).
- Repository pattern katta loyihada (ORM abstraksiya — 2.12, 9).
- Prisma global module (PrismaService — 2.8).
9. Amaliy loyiha: "NestJS + DB CRUD API"
NestJS DB integratsiyasini mustahkamlash.
Maqsad
NestJS'ni DB'ga ulab (TypeORM yoki Prisma), to'liq CRUD API qurish: modul, entity/schema, repository inject, relations, tranzaksiya.
Talablar (requirements)
- DB ulanish: forRootAsync + ConfigService (TypeORM) yoki PrismaService (Misol 1, 4, 2.4, 2.8).
- Entity/Schema: User + Order (relations — 6.13/6.12/6.2).
- Feature modul: forFeature + inject (Misol 2, 2.6).
- CRUD service: repository/prisma; built-in exceptions; sahifalash (Misol 2, 4, 2.7).
- Controller: yupqa (ORM bilmaydi — Misol 7, 8.1: 2.7).
- Relations: eager/include (Misol 3, 5).
- Tranzaksiya: buyurtma + zaxira (Misol 5, 2.13).
- Migration: synchronize: false + migration 6.14-bob.
- Xavfsizlik: parol select: false; kalitlar .env (14).
- (Bonus) Repository pattern: interface + DI (Misol 8, 2.12).
Maslahatlar (hint)
- forRootAsync + ConfigService (2.4, 2-xato).
- forFeature + @InjectRepository/@InjectModel (2.6, 3-xato).
- Controller yupqa (service orqali — 8.1: 2.7, 4-xato).
- synchronize: false (6.13: 2.13, 1-xato).
- Prisma: PrismaService extends PrismaClient 2.8-bob.
- Tranzaksiya: $transaction/DataSource 2.13-bob.
"Tayyor" mezonlari (acceptance criteria)
- DB ulanadi (env'dan — async).
- Entity/Schema (relations).
- Feature modul (forFeature + inject).
- CRUD (built-in exceptions, sahifalash).
- Controller yupqa (ORM-agnostik).
- Relations (eager/include).
- Tranzaksiya.
- Migration (synchronize: false).
- Parol select: false; kalitlar .env.
- (Bonus) Repository pattern.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda NestJS'ni DB'ga ulashni o'rgandik:
- Integratsiya naqshi (forRoot entity/schema forFeature + inject — 2.1); ORM tanlovi (TypeORM/Prisma/Mongoose — 2.2).
- TypeORM (forRoot/forRootAsync — 2.3, 2.4; entity — 2.5; forFeature + @InjectRepository — 2.6; CRUD — 2.7).
- Prisma (PrismaService wrappersiz — 2.8; global modul; service — 2.9); Mongoose (forRoot/forFeature + @InjectModel — 2.10, 2.11).
- Tranzaksiya (DataSource/$transaction/QueryRunner — 2.13); connection pooling (2.4a); raw SQL (DataSource — 2.7a); graceful shutdown (2.8a); migration 2.14-bob; test mock 2.15-bob; repository pattern (2.12, 9).
Keyingi bob — 8.4-bob: Relations (1:1, 1:N, N:M). DB ulanishni bildik; endi NestJS'da bog'lanishlarni — one-to-one, one-to-many, many-to-many — chuqur o'rganamiz: entity relations, eager/lazy loading, cascade, va relations bilan CRUD. Bu — real ilovaning ma'lumot modeli (foydalanuvchi-buyurtma-mahsulot).
Foydalanilgan rasmiy/ishonchli manbalar
- docs.nestjs.com/techniques/database (TypeORM), /recipes/prisma, /techniques/mongodb
- prisma.io/docs/guides/frameworks/nestjs (PrismaService); @nestjs/typeorm, @nestjs/mongoose
- Prisma vs TypeORM taqqoslashi (NestJS kontekstida, 2026 holati)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!