NestJS

Ma'lumotlar bazasi (TypeORM)

Haqiqiy ilovalar ma'lumotni doimiy saqlashi kerak โ€” bu esa ma'lumotlar bazasini talab qiladi. NestJS eng mashhur ORM (Object-Relational Mapping) kutubxonalari bilan chuqur integratsiyaga ega. Ushbu darsda TypeORM orqali bazaga ulanishni, @Entity bilan jadval yaratishni, Repository shablonini va to'liq CRUD servisni yozishni o'rganamiz.

ORM nima va nega TypeORM?

ORM โ€” bu ma'lumotlar bazasidagi jadvallarni kodingizdagi obyektlar bilan bog'lovchi qatlam. U sizni SQL so'rovlarini qo'lda yozishdan qutqaradi: siz obyektlar bilan ishlaysiz, ORM esa ularni SQL'ga o'giradi. TypeORM โ€” TypeScript uchun eng mashhur ORM'lardan biri va NestJS bilan rasmiy integratsiyaga ega.

Ushbu darsda misollar uchun PostgreSQLni ishlatamiz, lekin sozlamada bazani almashtirish orqali istalgan bazaga o'tish mumkin. TypeORM'ning kuchli tomoni โ€” kodingiz baza turidan deyarli mustaqil bo'ladi.

O'rnatish va ulanish

Avval kerakli paketlarni o'rnatamiz: NestJS'ning TypeORM adapteri, TypeORM'ning o'zi va PostgreSQL drayveri:

npm install @nestjs/typeorm typeorm pg

Endi bazaga ulanishni ildiz modulda sozlaymiz. TypeOrmModule.forRoot() ulanish parametrlarini qabul qiladi:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'parol',
      database: 'mening_bazam',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,  // Faqat ishlab chiqish (development) uchun!
    }),
  ],
})
export class AppModule {}
synchronize: true baza sxemasini entity'laringizga qarab avtomatik yangilaydi. Bu ishlab chiqishda qulay, lekin ishlab chiqarishda (production) hech qachon ishlatmang โ€” u ma'lumotni yo'q qilishi mumkin. Production'da migratsiyalardan foydalaning.

@Entity โ€” jadval e'loni

Entity โ€” bu ma'lumotlar bazasidagi jadvalni ifodalovchi klass. Har bir entity nusxasi jadvalning bir qatoriga, har bir xossa esa ustunga mos keladi. Entity'ni @Entity dekoratori bilan belgilaymiz:

// src/users/user.entity.ts
import {
  Entity, PrimaryGeneratedColumn, Column, CreateDateColumn,
} from 'typeorm';

@Entity('users')   // 'users' nomli jadval
export class User {
  @PrimaryGeneratedColumn()   // Avtomatik o'suvchi asosiy kalit (id)
  id: number;

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

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

  @Column({ default: true })
  faol: boolean;

  @CreateDateColumn()   // Yaratilgan sana avtomatik qo'yiladi
  yaratilganSana: Date;
}

Asosiy ustun dekoratorlari:

Repository shabloni

Repository โ€” bu ma'lum bir entity bilan ishlash uchun tayyor obyekt. U find, save, delete kabi metodlar orqali bazaga so'rov yuboradi. TypeORM har bir entity uchun avtomatik repository yaratadi.

Repository'ni service ichida ishlatish uchun ikki bosqich kerak. Avval feature modulda entity'ni ro'yxatdan o'tkazamiz โ€” TypeOrmModule.forFeature() orqali:

// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  imports: [TypeOrmModule.forFeature([User])],  // User repository'sini ochadi
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}

So'ng service ichida repository'ni @InjectRepository dekoratori orqali injektsiya qilamiz:

// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>,
  ) {}
}
Repository<User> โ€” bu generik tip. U repository aynan User entity bilan ishlashini bildiradi. Shu tufayli find yoki save chaqirganingizda TypeScript sizga to'g'ri tiplarni ko'rsatadi.

To'liq CRUD service misoli

Endi barcha CRUD amallarini (Create, Read, Update, Delete) o'z ichiga olgan to'liq servisni yozamiz. Repository metodlariga e'tibor bering โ€” ular deyarli SQL yozmasdan hamma ishni bajaradi:

// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly repo: Repository<User>,
  ) {}

  // CREATE โ€” yangi foydalanuvchi yaratish
  async create(dto: CreateUserDto): Promise<User> {
    const user = this.repo.create(dto);  // Obyekt tayyorlaydi
    return this.repo.save(user);         // Bazaga yozadi
  }

  // READ (barchasi)
  async findAll(): Promise<User[]> {
    return this.repo.find();
  }

  // READ (bittasi)
  async findOne(id: number): Promise<User> {
    const user = await this.repo.findOne({ where: { id } });
    if (!user) {
      throw new NotFoundException('Foydalanuvchi topilmadi: ' + id);
    }
    return user;
  }

  // UPDATE
  async update(id: number, dto: Partial<CreateUserDto>): Promise<User> {
    const user = await this.findOne(id);   // Avval borligini tekshiramiz
    Object.assign(user, dto);              // Yangi qiymatlarni qo'shamiz
    return this.repo.save(user);
  }

  // DELETE
  async remove(id: number): Promise<void> {
    const user = await this.findOne(id);
    await this.repo.remove(user);
  }
}

Eng ko'p ishlatiladigan repository metodlari:

findOne topa olmasa null qaytaradi. Shuning uchun yuqoridagi kodda biz uni tekshirib, NotFoundException tashladik. NestJS bu istisnoni avtomatik 404 Not Found javobiga o'giradi โ€” bu qulay tayyor mexanizm.

Controller bilan bog'lash

Endi servisni controller'ga ulaymiz. Controller ingichka qoladi โ€” u faqat so'rovni qabul qilib, servisga uzatadi:

// src/users/users.controller.ts
import {
  Controller, Get, Post, Patch, Delete, Body, Param, ParseIntPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly service: UsersService) {}

  @Post()
  create(@Body() dto: CreateUserDto) {
    return this.service.create(dto);
  }

  @Get()
  findAll() {
    return this.service.findAll();
  }

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.service.findOne(id);
  }

  @Delete(':id')
  remove(@Param('id', ParseIntPipe) id: number) {
    return this.service.remove(id);
  }
}

Prisma bilan qisqacha taqqoslash

TypeORM yagona tanlov emas. So'nggi yillarda Prisma juda mashhur bo'ldi. U boshqacha yondashuvni tanlaydi: entity klasslari o'rniga alohida schema.prisma faylida sxema e'lon qilinadi va undan tiplar avtomatik generatsiya qilinadi.

// schema.prisma faylida sxema (TypeScript emas):
model User {
  id    Int     @id @default(autoincrement())
  ism   String
  email String  @unique
  faol  Boolean @default(true)
}
Yangi loyihalarda ko'p jamoalar Prisma'ni tanlaydi, chunki uning tip xavfsizligi va migratsiya tizimi kuchli. TypeORM esa NestJS bilan tabiiy uyg'unligi va rasmiy integratsiyasi tufayli hali ham juda mashhur. Ikkovi ham to'g'ri tanlov.

Xulosa