Prisma ORM + PostgreSQL — CRUD
Prisma nima?
Prisma — yangi avlod ORM. TypeORM dan asosiy farqi: modellar TypeScript sinfida emas, alohida .prisma faylda yoziladi. Shu fayldan TypeScript tiplari avtomatik generatsiya qilinadi.
TypeORM: Prisma:
──────────────────────────────────────────────
user.entity.ts schema.prisma
(TypeScript sinfi + dekoratorlar) (Prisma tili)
│ │
▼ ▼
TypeScript tip TypeScript tip
(qo'lda yoziladi) (avtomatik yaratiladi)
Prisma generatsiya qilgan tiplar — 100% baza bilan mos. Qo'lda yozilganda mos kelmaslik ehtimoli bor.
O'rnatish
Prisma 7.x da PrismaClient uchun driver adapter talab qilinadi. PostgreSQL uchun @prisma/adapter-pg va pg o'rnatiladi:
npm install prisma @prisma/adapter-pg pg
npm install -D @types/pg
npx prisma init
npx prisma init quyidagi fayllarni yaratadi:
loyiha/
├── prisma/
│ └── schema.prisma ← modellar shu yerda yoziladi
└── .env ← DATABASE_URL qo'shiladi
.env ga baza manzili yoziladi:
DATABASE_URL="postgresql://postgres:12345@localhost:5432/mydb?schema=public"
tsconfig.json
NestJS 11.x va Prisma 7.x bilan ishlash uchun tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"ignoreDeprecations": "6.0",
"esModuleInterop": true,
"isolatedModules": true,
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2023",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": false,
"skipLibCheck": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"strictBindCallApply": false,
"noFallthroughCasesInSwitch": false
}
}
💡
ignoreDeprecations: "6.0"— Prisma 7.x da ba'zi TypeScript ogohlantirishlarini bosadi.baseUrl: "./"—generated/prisma/clientimport yo'lini to'g'ri ishlashi uchun kerak.
schema.prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
output = "../generated/prisma/client"
// Prisma 7.x da output ko'rsatish — loyiha ildizida generated/ papkasi yaratiladi
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
Asosiy atributlar:
| Atribut | Ma'nosi |
|---|---|
@id |
Asosiy kalit |
@default(autoincrement()) |
Avtomatik o'suvchi son |
@unique |
Takrorlanmasin |
@default(now()) |
Yaratilganda joriy vaqt |
@updatedAt |
Yangilanganda avtomatik vaqt |
@@map("users") |
Jadval nomi (standart: "User") |
Migration va generatsiya
npx prisma migrate dev --name init
Bu buyruq:
- Schema dan migration SQL fayl yozadi
- Migration ni bazaga qo'llaydi
- TypeScript tiplarini
generated/prisma/clientga generatsiya qiladi
prisma/
├── schema.prisma
└── migrations/
└── 20240101000000_init/
└── migration.sql ← Prisma yozgan SQL
generated/
└── prisma/
└── client/ ← avtomatik yaratilgan TypeScript tiplari
Prisma yozgan SQL ga o'xshab:
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL
);
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
PrismaService — NestJS ga ulash
Prisma 7.x da PrismaClient konstruktoriga adapter beriladi:
// src/prisma/prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from 'generated/prisma/client';
// Eslatma: '@prisma/client' emas — 'generated/prisma/client'
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
constructor() {
super({
adapter: new PrismaPg({
connectionString: process.env.DATABASE_URL,
}),
});
}
async onModuleInit() {
await this.$connect();
// NestJS moduli yuklanganda — bazaga ulanadi
}
async onModuleDestroy() {
await this.$disconnect();
// NestJS to'xtaganda — ulanishni yopadi
}
}
Adapter nima o'zi?
Tasavvur qil: telefoning bor, lekin u European rozеtkaga ulanmaydi. Shuning uchun adapter ishlatasan — oradagi "tarjimon".
Telefon → Adapter → Rozetka
Prisma → Adapter → PostgreSQL drayveri (pg)
Prisma o'zi bazaga to'g'ridan-to'g'ri ulanishni bilmaydi. U faqat "men shu so'rovni bajarmoqchiman" deydi. Adapter esa shu so'rovni PostgreSQL ga tushunarli qilib "tarjima" qiladi va natijani qaytaradi.
Eskida Prisma ichida o'zi bitta drayver bor edi (faqat PostgreSQL uchun). Endi Prisma universal bo'ldi — MySQL, SQLite, MongoDB ham qo'llab-quvvatlanadi. Shuning uchun "qaysi baza uchun ishlaysan?" deb adapter talab qiladi.
extends PrismaClient — meros olish
export class PrismaService extends PrismaClient
extends — bu meros olish. PrismaService PrismaClient ning barcha metodlarini o'ziga oladi, ustiga NestJS ga mos qo'shimchalar bilan:
PrismaClient (ota-klass)
- this.user.findMany()
- this.user.create()
- this.$connect()
- this.$disconnect()
PrismaService (bola-klass)
- yuqoridagi hamma narsani meros oladi
+ onModuleInit() ← yangi qo'shildi
+ onModuleDestroy() ← yangi qo'shildi
constructor va super() — nima bo'lyapti?
constructor() {
super({
adapter: new PrismaPg({
connectionString: process.env.DATABASE_URL,
}),
});
}
super() — ota-klassning (PrismaClient) constructor ini chaqirish. Bola-klass ota-klassni o'zi xohlagan parametrlar bilan "yoqadi":
new PrismaService() chaqirildi
│
▼
constructor() ishga tushdi
│
▼
super() — ota-klass (PrismaClient) ishga tushirildi
│
▼
PrismaClient ichiga adapter berildi:
"Sen PostgreSQL bilan ishlaysan,
manzil: DATABASE_URL"
new PrismaPg({ connectionString: ... }) — PostgreSQL adapter yaratilmoqda. process.env.DATABASE_URL esa .env faylidagi qiymatni o'qiydi:
DATABASE_URL="postgresql://postgres:12345@localhost:5432/mydb"
implements OnModuleInit, OnModuleDestroy — nima?
Bu NestJS ga aytilmoqda: "Men ikkita maxsus metodga egaman, vaqti kelganda o'zing chaqir."
NestJS ishga tushdi
│
▼
onModuleInit() ← NestJS o'zi chaqiradi
$connect() — baza ulanishi o'rnatiladi
│
▼
... ilova ishlaydi ...
│
▼
NestJS to'xtatildi (Ctrl+C)
│
▼
onModuleDestroy() ← NestJS o'zi chaqiradi
$disconnect() — ulanish yopiladi
Baza ulanishini qo'lda boshqarish kerak emas — NestJS o'zi "vaqti keldi" deb aytadi.
// src/prisma/prisma.module.ts
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
// @Global() — bir marta import qilsak, barcha modullarda avtomatik mavjud
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
@Global() — nima uchun kerak?
@Global() bo'lmasa — har bir modulda alohida import qilish kerak:
UsersModule → PrismaModule import kerak
ProductsModule → PrismaModule import kerak
OrdersModule → PrismaModule import kerak
...
@Global() bilan — faqat AppModule da bir marta import qilish yetarli:
AppModule → PrismaModule (bir marta)
UsersModule → to'g'ridan-to'g'ri PrismaService ishlatsa bo'ladi
ProductsModule → to'g'ridan-to'g'ri PrismaService ishlatsa bo'ladi
OrdersModule → to'g'ridan-to'g'ri PrismaService ishlatsa bo'ladi
// src/app.module.ts
import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma/prisma.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [PrismaModule, UsersModule],
})
export class AppModule {}
Hammasi birgalikda qanday ishlaydi?
npx nest start
│
▼
AppModule yuklandi
→ PrismaModule yuklandi (@Global — butun ilovaga tarqaldi)
→ PrismaService yaratildi
→ constructor() → PrismaClient(adapter: PrismaPg) tayyor
→ onModuleInit() → $connect() — baza ulanishi o'rnatildi
→ UsersModule yuklandi
→ UsersService yaratildi
→ PrismaService inject bo'ldi (import qilmasdan, @Global bo'lgani uchun)
│
▼
GET /users → usersService.findAll() → prisma.user.findMany()
│
▼
PrismaPg adapter
│
▼
PostgreSQL — SQL bajarildi
│
▼
Natija qaytdi
| Narsa | Nima qiladi |
|---|---|
adapter |
Prisma ↔ PostgreSQL orasidagi "tarjimon" |
extends PrismaClient |
PrismaService barcha Prisma metodlarini oladi |
super({ adapter }) |
Ota-klassga "qaysi baza, qaysi manzil" deb aytadi |
onModuleInit |
NestJS yoqilganda baza ulanishini o'rnatadi |
onModuleDestroy |
NestJS o'chirilganda ulanishni yopadi |
@Global() |
Bir marta import — hamma yerda ishlaydi |
DTO
// src/users/dto/create-user.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
}
// src/users/dto/update-user.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}
// PartialType — barcha maydonlarni ixtiyoriy (optional) qiladi
UsersService — CRUD
// src/users/users.service.ts
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { User } from 'generated/prisma/client';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaService) {}
// PrismaService inject qilindi — @Global() bo'lgani uchun UsersModule ga import kerak emas
create
async create(dto: CreateUserDto): Promise<User> {
const exists = await this.prisma.user.findUnique({
where: { email: dto.email },
});
// findUnique — @unique yoki @id maydon bo'yicha qidiradi
// Natija: User | null
if (exists) {
throw new ConflictException('Bu email allaqachon mavjud');
}
return this.prisma.user.create({ data: dto });
}
findAll
async findAll(): Promise<User[]> {
return this.prisma.user.findMany({
orderBy: { createdAt: 'desc' },
});
}
findOne
async findOne(id: number): Promise<User> {
const user = await this.prisma.user.findUnique({
where: { id },
});
if (!user) {
throw new NotFoundException(`${id}-foydalanuvchi topilmadi`);
}
return user;
}
update
async update(id: number, dto: UpdateUserDto): Promise<User> {
await this.findOne(id); // avval mavjudligini tekshiramiz
return this.prisma.user.update({
where: { id },
data: dto,
// PartialType bilan mos keladi — faqat kelgan maydonlar yangilanadi
});
}
remove
async remove(id: number): Promise<void> {
await this.findOne(id);
await this.prisma.user.delete({ where: { id } });
}
}
UsersController
// src/users/users.controller.ts
import {
Controller, Get, Post, Patch, Delete,
Param, Body, ParseIntPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
@Patch(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateUserDto,
) {
return this.usersService.update(id, dto);
}
@Delete(':id')
remove(@Param('id', ParseIntPipe) id: number) {
return this.usersService.remove(id);
}
}
UsersModule
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
// PrismaModule @Global() — bu yerda import qilish shart emas
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
TypeORM ga nisbatan farq: TypeOrmModule.forFeature([User]) kerak emas — Prisma da repository tushunchasi yo'q, to'g'ridan-to'g'ri prisma.user orqali ishlaydi.
Foydali buyruqlar
# Development da migration
npx prisma migrate dev --name add_phone_to_users
# Production da migration bajarish
npx prisma migrate deploy
# Prisma Studio — brauzerda bazani ko'rish
npx prisma studio
# http://localhost:5555 da ochiladi
# Faqat TypeScript tiplarini qayta generatsiya qilish
npx prisma generate
💡
migrate devbuyrug'iprisma generateni ham o'zi bajaradi. Alohidagenerate— schema o'zgarganda lekin migration kerak bo'lmaganda ishlatiladi.
Type-safe queries — Prisma ning kuchi
// TypeORM da imlo xatosi kompilyatsiyada sezilmaydi — runtime da topiladi
userRepository.findOne({ where: { emial: 'test' } });
// Prisma da — kompilyatsiyada xato beradi
this.prisma.user.findUnique({ where: { emial: 'test' } });
// ^^^^^ TypeScript xato beradi:
// "Object literal may only specify known properties,
// and 'emial' does not exist in type 'UserWhereUniqueInput'"
select — keraksiz maydonlarni yashirish
// Faqat id, name, email qaytarish
const users = await this.prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
// createdAt, updatedAt kelmaydi
},
});
select ishlatilsa — faqat belgilangan maydonlar qaytadi. Parol kabi sezgir ma'lumotlarni response dan chiqarmaslik uchun qulay.
Loyiha tuzilishi
src/
├── prisma/
│ ├── prisma.service.ts ← PrismaClient extends, adapter bilan
│ └── prisma.module.ts ← @Global() modul
├── users/
│ ├── dto/
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── users.module.ts
└── app.module.ts
prisma/
├── schema.prisma
└── migrations/
generated/
└── prisma/
└── client/ ← avtomatik yaratilgan tiplar (git ga qo'shmaslik)
⚠️
generated/papkasini.gitignorega qo'shing — har kimning mashinasidaprisma generatebilan qayta yaratiladi.
Vazifalar
1-vazifa: schema.prisma ga Product modeli qo'shing (id, name, price Float, createdAt). Migration yarating.
2-vazifa: ProductsService yozing — create, findAll, findOne, update, remove metodlari.
3-vazifa: findAll da select qo'shing — faqat id, name, price qaytsin.
Xulosa
| Prisma | TypeORM analogi |
|---|---|
schema.prisma |
*.entity.ts |
prisma generate |
— (avtomatik) |
prisma migrate dev |
migration:generate + run |
prisma.user.create |
userRepository.save |
prisma.user.findUnique |
userRepository.findOne |
prisma.user.findMany |
userRepository.find |
prisma.user.update |
userRepository.save |
prisma.user.delete |
userRepository.remove |
Prisma 7.x asosiy o'zgarishlar:
Eski (@prisma/client) |
Yangi (Prisma 7.x) |
|---|---|
npm install @prisma/client |
@prisma/adapter-pg va pg ham kerak |
new PrismaClient() |
new PrismaClient({ adapter: new PrismaPg(...) }) |
from '@prisma/client' |
from 'generated/prisma/client' |
Keyingi dars
20-dars: Prisma — Relations (Bog'liqliklar)