Providers va Dependency Injection
Muammodan boshlaylik
Siz do'kon uchun backend yozyapsiz. Controller da mahsulotlar ro'yxatini qaytarish kerak. Shoshib qolsangiz, shunday yozib qo'yishingiz mumkin:
@Controller("products")
export class ProductsController {
@Get()
findAll() {
// Ma'lumotlar bazasidan olish...
// Filtirlash...
// Tartiblash...
// Hisoblash...
return result;
}
}
Avvaliga ishlaydi. Lekin bir haftadan keyin:
- Boshqa controller ham mahsulotlarga murojaat qilishi kerak bo'ladi
- O'sha logikani yana yozasiz — kod takrorlanadi
- Logikani o'zgartirsangiz — ikki joyni o'zgartirish kerak
- Test yozish deyarli imkonsiz bo'lib qoladi
Aynan shu muammoni hal qilish uchun NestJS da Provider tushunchasi bor.
Provider nima?
Provider — bu NestJS ga "boshqar" deb topshirilgan oddiy TypeScript sinfi. Ko'pincha Service ko'rinishida bo'ladi.
Qoidasi oddiy: Controller faqat so'rov qabul qiladi, Service esa ishni bajaradi.
Hayotiy misol bilan tushuntirsak — siz shifoxonaga borasiz. Qabulxonada registratura xodimi sizni qabul qiladi, hujjatlaringizni oladi. Lekin u davolamaydi. Davolash — shifokorning ishi. Registratura (Controller) va shifokor (Service) — ikki alohida rol.
Birinchi Service
CLI bilan yaratamiz:
nest g s users --no-spec
--no-spec — test faylini yaratmaslik uchun. CLI users.service.ts faylini yaratib, users.module.ts dagi providers massiviga avtomatik qo'shadi.
import { Injectable } from "@nestjs/common";
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: "Jasur", email: "jasur@example.com" },
{ id: 2, name: "Malika", email: "malika@example.com" },
{ id: 3, name: "Bobur", email: "bobur@example.com" },
];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find((user) => user.id === id);
}
create(data: { name: string; email: string }) {
const newUser = {
id: this.users.length + 1,
...data,
};
this.users.push(newUser);
return newUser;
}
remove(id: number) {
this.users = this.users.filter((user) => user.id !== id);
return { message: `${id}-foydalanuvchi o'chirildi` };
}
}
@Injectable() — bu decorator NestJS ga shuni aytadi: "bu sinfni men boshqaraman, kerak bo'lganda o'zim yarataman va uzataman."
Dependency Injection — eng muhim tushuncha
Endi Service ni Controller ga ulash kerak. Ko'pchilik birinchi marta shunday qiladi:
@Controller("users")
export class UsersController {
// ❌ Noto'g'ri — o'zingiz yaratmang
private usersService = new UsersService();
}
Bu noto'g'ri. Nima uchun?
Tasavvur qiling — siz uyga kirish uchun har safar yangi eshik yasayapsiz. Uyda mingta eshik bo'lib ketadi. Mantiqsiz, to'g'rimi?
To'g'ri yo'l — Dependency Injection. NestJS Service ni o'zi yaratadi, o'zi saqlaydi va sizga tayyor holda uzatadi:
import {
Controller,
Get,
Post,
Delete,
Param,
Body,
} from "@nestjs/common";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
// ✅ To'g'ri — NestJS o'zi uzatadi
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(":id")
findOne(@Param("id") id: string) {
return this.usersService.findOne(+id);
}
@Post()
create(@Body() body: any) {
return this.usersService.create(body);
}
@Delete(":id")
remove(@Param("id") id: string) {
return this.usersService.remove(+id);
}
}
constructor(private readonly usersService: UsersService) — bu qator NestJS ga shuni aytadi: "menga UsersService kerak". NestJS uni topadi, yaratadi va uzatadi. Siz hech narsa qilmaysiz.
💡
+id— bu TypeScript da string ni number ga aylantirish.Paramdan kelgan qiymat har doim string bo'ladi, lekin bizningfindOnemethod number kutadi. Shuning uchun+qo'yamiz.
Dependency Injection qanday ishlaydi?
NestJS ichida katta bir "konteyner" bor. U barcha @Injectable() sinflarni biladi.
Qachonki siz constructor da biror service so'rasangiz — NestJS:
- "Bu service menda bormi?" — tekshiradi
- Bor bo'lsa — tayyor nusxasini beradi
- Yo'q bo'lsa — yangi yaratadi, saqlaydi, beradi
Eng muhimi — bir Service butun ilova davomida bitta nusxada mavjud bo'ladi. Bu Singleton pattern deyiladi. Xotirani tejaydi, hamma bir xil holatni ko'radi.
NestJS Konteyner
┌──────────────────────────────────┐
│ UsersService → 1 ta nusxa │
│ ProductsService → 1 ta nusxa │
│ OrdersService → 1 ta nusxa │
└──────────────────────────────────┘
↓ so'ragan controller ga uzatadi
Service ichida Service ishlatish
Haqiqiy loyihalarda Servicelar bir-birini ham ishlatadi. Masalan, OrdersService buyurtma yaratayotganda UsersService dan foydalanuvchini tekshirishi mumkin:
@Injectable()
export class OrdersService {
constructor(private readonly usersService: UsersService) {}
create(userId: number, productId: number) {
const user = this.usersService.findOne(userId);
if (!user) {
return { error: "Foydalanuvchi topilmadi" };
}
return {
user: user.name,
productId,
status: "yaratildi",
};
}
}
NestJS bu yerda ham o'zi hal qiladi — UsersService ni topib, OrdersService ga uzatadi.
Module ga ulash
Service yaratdingiz, Controller ga uladingiz — lekin bu yetarli emas. Module ham xabardor bo'lishi kerak:
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
@Module({
controllers: [UsersController],
providers: [UsersService], // ← shu yerga qo'shing
})
export class UsersModule {}
providers massiviga yozmagan bo'lsangiz — NestJS Service ni topolmaydi va xato beradi. CLI avtomatik qo'shadi, lekin qo'lda yozayotganda esdan chiqmasin.
Hammasi birgalikda — to'liq ko'rinish
So'rov keldi: GET /users/1
↓
UsersController.findOne(id: '1')
↓
UsersService.findOne(id: 1)
↓
{ id: 1, name: 'Jasur', email: 'jasur@example.com' }
↓
Javob foydalanuvchiga qaytdi
Controller faqat so'rovni qabul qildi va Service ga uzatdi. Service esa ma'lumotni topib qaytardi. Har birining roli aniq, har biri o'z ishini qildi.
Nima uchun bu muhim?
Tajribali dasturchilar Dependency Injection ni sevishining sababi bor:
Testlash oson. Service ni alohida test qilish mumkin. Controller ni alohida test qilish mumkin. Bir-biriga bog'liq emas.
O'zgartirish oson. Ertaga ma'lumotni fayl o'rniga ma'lumotlar bazasidan olmoqchi bo'lsangiz — faqat Service ni o'zgartirasiz. Controller da hech narsa o'zgarmaydi.
Kod takrorlanmaydi. UsersService ni 10 ta controller ishlatishi mumkin — lekin yozish faqat bir marta.
Xulosa
Provider — NestJS ning "ish qiluvchi" qismi. Controller so'rov qabul qiladi, Provider (Service) esa haqiqiy ishni bajaradi. Dependency Injection esa bu ikkalasini bog'lash uchun NestJS ning aqlli mexanizmi — siz new yozmaysiz, NestJS o'zi hal qiladi.
Eslab qoling:
@Injectable()→ bu sinf NestJS tomonidan boshqariladiconstructor(private service: Service)→ DI orqali olishproviders: [Service]→ modulda ro'yxatdan o'tkazish- Bir Service — butun ilova bo'ylab bitta nusxa
Keyingi dars
4-dars: Modules — Feature, Shared, Global, Dynamic