Retseptlar11 min read

Prisma

Prisma - bu Node.js va TypeScript uchun open-source ORM. U oddiy SQL yozish yoki SQL query builder'lar (knex.js kabi) yoxud ORM'lar (TypeORM va Sequelize kabi) singari boshqa datab

Prisma - bu Node.js va TypeScript uchun open-source ORM. U oddiy SQL yozish yoki SQL query builder'lar (knex.js kabi) yoxud ORM'lar (TypeORM va Sequelize kabi) singari boshqa database access vositalaridan foydalanishga muqobil sifatida ishlatiladi. Prisma hozirda PostgreSQL, MySQL, SQL Server, SQLite, MongoDB va CockroachDB'ni qo'llab-quvvatlaydi (Preview).

Prisma'ni oddiy JavaScript bilan ham ishlatish mumkin, ammo u TypeScript'ni faol qo'llab-quvvatlaydi va TypeScript ekotizimidagi boshqa ORM'larga qaraganda yuqoriroq type-safety beradi. Prisma va TypeORM'ning type-safety kafolatlari bo'yicha batafsil taqqoslashni bu yerda topishingiz mumkin.

Note

Prisma qanday ishlashini tezda tushunib olmoqchi bo'lsangiz, Quickstart ni kuzating yoki documentation ichidagi Introduction ni o'qing. Shuningdek, prisma-examples repository'sida REST va GraphQL uchun tayyor ishlaydigan misollar ham mavjud.

Boshlash

Ushbu recipe'da NestJS va Prisma bilan noldan qanday boshlashni o'rganasiz. Siz database'dan ma'lumot o'qiydigan va yozadigan REST API'ga ega namuna NestJS ilovasini qurasiz.

Ushbu qo'llanma uchun database server sozlash yukidan qochish maqsadida SQLite ishlatiladi. Agar siz PostgreSQL yoki MySQL ishlatayotgan bo'lsangiz ham bu qo'llanmadan foydalanishingiz mumkin - kerakli joylarda ularga oid qo'shimcha ko'rsatmalar beriladi.

Note

Agar sizda allaqachon loyiha bo'lsa va Prisma'ga o'tishni o'ylayotgan bo'lsangiz, mavjud loyihaga Prisma qo'shish bo'yicha qo'llanmadan foydalaning. Agar TypeORM'dan ko'chayotgan bo'lsangiz, Migrating from TypeORM to Prisma qo'llanmasini o'qing.

NestJS loyihasini yaratish

Boshlash uchun NestJS CLI'ni o'rnating va quyidagi buyruqlar bilan ilova skeleton'ini yarating:

Terminal
1$ npm install -g @nestjs/cli
2$ nest new hello-prisma

First steps sahifasini ko'rib, ushbu buyruq yaratgan loyiha fayllari haqida ko'proq bilib oling. Shuningdek, endi ilovani ishga tushirish uchun npm start ni ishlatishingiz mumkin. Hozir http://localhost:3000/ da ishlayotgan REST API faqat src/app.controller.ts ichida yozilgan bitta route'ga ega. Ushbu qo'llanma davomida siz users va posts haqidagi ma'lumotlarni saqlash va olish uchun qo'shimcha route'lar yaratib borasiz.

Prisma'ni sozlash

Avval Prisma CLI'ni loyiha ichiga development dependency sifatida o'rnating:

Terminal
1$ cd hello-prisma
2$ npm install prisma --save-dev

Keyingi qadamlar davomida Prisma CLI dan foydalanamiz. Best practice sifatida CLI'ni lokal ishlatish uchun oldiga npx qo'yish tavsiya etiladi:

Terminal
1$ npx prisma
Expand if you're using Yarn

Agar Yarn ishlatayotgan bo'lsangiz, Prisma CLI'ni quyidagicha o'rnatishingiz mumkin:

Terminal
1$ yarn add prisma --dev

O'rnatilgach, uni yarn prefiksi bilan chaqirishingiz mumkin:

Terminal
1$ yarn prisma

Endi Prisma CLI'dagi init buyrug'i yordamida boshlang'ich Prisma setup'ni yarating:

Terminal
1$ npx prisma init

Bu buyruq quyidagi tarkibga ega yangi prisma katalogini yaratadi:

  • schema.prisma: Database ulanishini ko'rsatadi va database schema'ni o'z ichiga oladi
  • prisma.config.ts: Loyihangiz uchun konfiguratsiya fayli
  • .env: Odatda database credential'larini environment variable'lar ko'rinishida saqlash uchun ishlatiladigan dotenv fayli

Generator output path'ni belgilash

Generated Prisma client uchun output path ni prisma init vaqtida --output ../src/generated/prisma orqali yoki to'g'ridan-to'g'ri Prisma schema ichida belgilang:

Groovy
1generator client {
2  provider        = "prisma-client"
3  output          = "../src/generated/prisma"
4}

Modul formatini sozlash

Generator ichida moduleFormat ni cjs ga o'rnating:

Groovy
1generator client {
2  provider        = "prisma-client"
3  output          = "../src/generated/prisma"
4  moduleFormat    = "cjs"
5}
Note

moduleFormat konfiguratsiyasi kerak, chunki Prisma v7 standart holatda ES module sifatida keladi va bu NestJS'ning CommonJS setup'i bilan mos kelmaydi. moduleFormat ni cjs ga o'rnatish Prisma'ni ESM o'rniga CommonJS modul generatsiya qilishga majbur qiladi.

Database ulanishini sozlash

Database ulanishi schema.prisma faylidagi datasource blokida sozlanadi. Standart holatda u postgresql ga o'rnatilgan bo'ladi, ammo bu qo'llanmada SQLite ishlatilgani uchun datasource blokidagi provider ni sqlite ga o'zgartirishingiz kerak:

Groovy
1datasource db {
2  provider = "sqlite"
3}
4
5generator client {
6  provider      = "prisma-client"
7  output        = "../src/generated/prisma"
8  moduleFormat  = "cjs"
9}

Endi .env faylini oching va DATABASE_URL environment variable'ini quyidagicha o'zgartiring:

Terminal
1DATABASE_URL="file:./dev.db"

ConfigModule sozlanganiga ishonch hosil qiling, aks holda DATABASE_URL o'zgaruvchisi .env dan olinmaydi.

SQLite database'lari oddiy fayllar bo'ladi; undan foydalanish uchun alohida server kerak emas. Shu sabab host va port bilan connection URL yozish o'rniga, bu yerda dev.db deb nomlangan lokal faylga ishora qilishingiz kifoya. Bu fayl keyingi qadamda yaratiladi.

Expand if you're using PostgreSQL, MySQL, MsSQL or Azure SQL

PostgreSQL va MySQL bilan connection URL'ni database server ga ishora qiladigan qilib sozlashingiz kerak. Kerakli connection URL formati haqida bu yerda ko'proq bilishingiz mumkin.

PostgreSQL

Agar PostgreSQL ishlatayotgan bo'lsangiz, schema.prisma va .env fayllarini quyidagicha o'zgartiring:

schema.prisma

Groovy
1datasource db {
2  provider = "postgresql"
3}
4
5generator client {
6  provider = "prisma-client"
7  output          = "../src/generated/prisma"
8  moduleFormat  = "cjs"
9}

.env

Terminal
1DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"

Barcha katta harflarda yozilgan placeholder'larni o'zingizning database credential'laringiz bilan almashtiring. Agar SCHEMA uchun nima yozishni bilmasangiz, ko'p hollarda standart qiymat public bo'ladi:

Terminal
1DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public"

PostgreSQL database'ni qanday sozlashni bilmoqchi bo'lsangiz, Heroku'da bepul PostgreSQL database sozlash qo'llanmasini kuzatishingiz mumkin.

MySQL

Agar MySQL ishlatayotgan bo'lsangiz, schema.prisma va .env fayllarini quyidagicha o'zgartiring:

schema.prisma

Groovy
1datasource db {
2  provider = "mysql"
3}
4
5generator client {
6  provider = "prisma-client"
7  output          = "../src/generated/prisma"
8  moduleFormat  = "cjs"
9}

.env

Terminal
1DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE"

Barcha katta harflarda yozilgan placeholder'larni database credential'laringiz bilan almashtiring.

Microsoft SQL Server / Azure SQL Server

Agar Microsoft SQL Server yoki Azure SQL Server ishlatayotgan bo'lsangiz, schema.prisma va .env fayllarini quyidagicha o'zgartiring:

schema.prisma

Groovy
1datasource db {
2  provider = "sqlserver"
3}
4
5generator client {
6  provider = "prisma-client"
7  output          = "../src/generated/prisma"
8  moduleFormat  = "cjs"
9}

.env

Barcha katta harflarda yozilgan placeholder'larni database credential'laringiz bilan almashtiring. Agar encrypt uchun nima yozishni bilmasangiz, ko'p hollarda standart qiymat true bo'ladi:

Terminal
1DATABASE_URL="sqlserver://HOST:PORT;database=DATABASE;user=USER;password=PASSWORD;encrypt=true"

Prisma Migrate bilan ikkita database jadvali yaratish

Ushbu bo'limda Prisma Migrate yordamida database'da ikkita yangi jadval yaratasiz. Prisma Migrate Prisma schema ichidagi deklarativ data model asosida SQL migration fayllarini generatsiya qiladi. Bu migration fayllari to'liq sozlanadi, shu sabab database'ning qo'shimcha xususiyatlarini yoki masalan seed qilish uchun qo'shimcha buyruqlarni ham qo'shishingiz mumkin.

schema.prisma fayliga quyidagi ikkita modelni qo'shing:

Groovy
1model User {
2  id    Int     @default(autoincrement()) @id
3  email String  @unique
4  name  String?
5  posts Post[]
6}
7
8model Post {
9  id        Int      @default(autoincrement()) @id
10  title     String
11  content   String?
12  published Boolean? @default(false)
13  author    User?    @relation(fields: [authorId], references: [id])
14  authorId  Int?
15}

Prisma modellari tayyor bo'lgach, SQL migration fayllarini generatsiya qilib, ularni database'ga qo'llashingiz mumkin. Terminalda quyidagi buyruqni ishga tushiring:

Terminal
1$ npx prisma migrate dev --name init

Bu prisma migrate dev buyrug'i SQL fayllarini generatsiya qiladi va ularni to'g'ridan-to'g'ri database'ga qo'llaydi. Bu holatda mavjud prisma katalogida quyidagi migration fayllari yaratiladi:

Terminal
1$ tree prisma
2prisma
3├── dev.db
4├── migrations
5│   └── 20201207100915_init
6│       └── migration.sql
7└── schema.prisma
Expand to view the generated SQL statements

SQLite database'ingizda quyidagi jadvallar yaratiladi:

Sql
1-- CreateTable
2CREATE TABLE "User" (
3    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4    "email" TEXT NOT NULL,
5    "name" TEXT
6);
7
8-- CreateTable
9CREATE TABLE "Post" (
10    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
11    "title" TEXT NOT NULL,
12    "content" TEXT,
13    "published" BOOLEAN DEFAULT false,
14    "authorId" INTEGER,
15
16    FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
17);
18
19-- CreateIndex
20CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");

Prisma Client'ni o'rnatish va generatsiya qilish

Prisma Client - bu Prisma model ta'rifidan generatsiya qilinadigan type-safe database client. Shu yondashuv sabab Prisma Client aynan modellaringizga moslashtirilgan CRUD operatsiyalarini taqdim etadi.

Prisma Client'ni loyihaga o'rnatish uchun terminalda quyidagi buyruqni ishga tushiring:

Terminal
1$ npm install @prisma/client

O'rnatilgach, loyiha uchun kerakli type'lar va Client'ni generatsiya qilish uchun generate buyrug'ini ishga tushiring. Schema'ga o'zgartirish kiritilsa, type'larni mos holda saqlash uchun generate buyrug'ini yana qayta ishga tushirishingiz kerak bo'ladi.

Terminal
1$ npx prisma generate

Prisma Client'dan tashqari, ishlatayotgan database turiga mos driver adapter ham kerak bo'ladi. SQLite uchun @prisma/adapter-better-sqlite3 driver'ini o'rnatishingiz mumkin.

Terminal
1npm install @prisma/adapter-better-sqlite3
Expand if you're using PostgreSQL, MySQL, MsSQL, or AzureSQL
  • For PostgreSQL
Terminal
1npm install @prisma/adapter-pg
  • For MySQL, MsSQL, AzureSQL:
Terminal
1npm install @prisma/adapter-mariadb

Prisma Client'ni NestJS service'larida ishlatish

Endi Prisma Client yordamida database query'larini yubora olasiz. Prisma Client bilan query yozish haqida ko'proq bilish uchun API documentation ni ko'ring.

NestJS ilovasini sozlayotganda, database query'lari uchun Prisma Client API'ni service ichida abstraksiya qilish ma'qul. Buning uchun PrismaClient instansiyasini yaratish va database'ga ulanishni boshqaradigan yangi PrismaService yarating.

src katalogi ichida prisma.service.ts nomli yangi fayl yarating va unga quyidagi kodni yozing:

TypeScript
1import { Injectable } from '@nestjs/common';
2import { PrismaClient } from './generated/prisma/client';
3import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
4
5@Injectable()
6export class PrismaService extends PrismaClient {
7  constructor() {
8    const adapter = new PrismaBetterSqlite3({ url: process.env.DATABASE_URL });
9    super({ adapter });
10  }
11}

Keyin Prisma schema'dagi User va Post modellari uchun database chaqiruvlarini bajaradigan service'larni yozishingiz mumkin.

Yana src katalogi ichida user.service.ts nomli yangi fayl yarating va unga quyidagi kodni yozing:

TypeScript
1import { Injectable } from '@nestjs/common';
2import { PrismaService } from './prisma.service';
3import { User, Prisma } from 'generated/prisma';
4
5@Injectable()
6export class UsersService {
7  constructor(private prisma: PrismaService) {}
8
9  async user(
10    userWhereUniqueInput: Prisma.UserWhereUniqueInput,
11  ): Promise<User | null> {
12    return this.prisma.user.findUnique({
13      where: userWhereUniqueInput,
14    });
15  }
16
17  async users(params: {
18    skip?: number;
19    take?: number;
20    cursor?: Prisma.UserWhereUniqueInput;
21    where?: Prisma.UserWhereInput;
22    orderBy?: Prisma.UserOrderByWithRelationInput;
23  }): Promise<User[]> {
24    const { skip, take, cursor, where, orderBy } = params;
25    return this.prisma.user.findMany({
26      skip,
27      take,
28      cursor,
29      where,
30      orderBy,
31    });
32  }
33
34  async createUser(data: Prisma.UserCreateInput): Promise<User> {
35    return this.prisma.user.create({
36      data,
37    });
38  }
39
40  async updateUser(params: {
41    where: Prisma.UserWhereUniqueInput;
42    data: Prisma.UserUpdateInput;
43  }): Promise<User> {
44    const { where, data } = params;
45    return this.prisma.user.update({
46      data,
47      where,
48    });
49  }
50
51  async deleteUser(where: Prisma.UserWhereUniqueInput): Promise<User> {
52    return this.prisma.user.delete({
53      where,
54    });
55  }
56}

E'tibor bering, bu yerda service tashqariga chiqaradigan metodlar to'g'ri typed bo'lishi uchun Prisma Client generatsiya qilgan type'lardan foydalanilyapti. Shu bilan model type'larini qo'lda yozish va alohida interface yoki DTO fayllari yaratishdagi boilerplate kamayadi.

Endi xuddi shu ishni Post modeli uchun ham bajaring.

Yana src katalogi ichida post.service.ts nomli yangi fayl yarating va unga quyidagi kodni yozing:

TypeScript
1import { Injectable } from '@nestjs/common';
2import { PrismaService } from './prisma.service';
3import { Post, Prisma } from 'generated/prisma';
4
5@Injectable()
6export class PostsService {
7  constructor(private prisma: PrismaService) {}
8
9  async post(
10    postWhereUniqueInput: Prisma.PostWhereUniqueInput,
11  ): Promise<Post | null> {
12    return this.prisma.post.findUnique({
13      where: postWhereUniqueInput,
14    });
15  }
16
17  async posts(params: {
18    skip?: number;
19    take?: number;
20    cursor?: Prisma.PostWhereUniqueInput;
21    where?: Prisma.PostWhereInput;
22    orderBy?: Prisma.PostOrderByWithRelationInput;
23  }): Promise<Post[]> {
24    const { skip, take, cursor, where, orderBy } = params;
25    return this.prisma.post.findMany({
26      skip,
27      take,
28      cursor,
29      where,
30      orderBy,
31    });
32  }
33
34  async createPost(data: Prisma.PostCreateInput): Promise<Post> {
35    return this.prisma.post.create({
36      data,
37    });
38  }
39
40  async updatePost(params: {
41    where: Prisma.PostWhereUniqueInput;
42    data: Prisma.PostUpdateInput;
43  }): Promise<Post> {
44    const { data, where } = params;
45    return this.prisma.post.update({
46      data,
47      where,
48    });
49  }
50
51  async deletePost(where: Prisma.PostWhereUniqueInput): Promise<Post> {
52    return this.prisma.post.delete({
53      where,
54    });
55  }
56}

Hozircha UsersService va PostsService Prisma Client'da mavjud CRUD query'larini o'rab turibdi. Real ilovada service qatlami business logic'ni ham shu yerga joylashtirish uchun ishlatiladi. Masalan, UsersService ichida foydalanuvchi parolini yangilaydigan updatePassword metodini yozishingiz mumkin.

Yangi service'larni app module ichida ro'yxatdan o'tkazishni unutmang.

REST API route'larini asosiy app controller'da implement qilish

Oxirida oldingi bo'limlarda yaratgan service'laringizdan foydalanib, ilovangizning turli route'larini implement qilasiz. Ushbu qo'llanma uchun barcha route'lar allaqachon mavjud AppController class'i ichiga joylashtiriladi.

app.controller.ts fayli tarkibini quyidagi kod bilan almashtiring:

TypeScript
1import {
2  Controller,
3  Get,
4  Param,
5  Post,
6  Body,
7  Put,
8  Delete,
9} from '@nestjs/common';
10import { UsersService } from './user.service';
11import { PostsService } from './post.service';
12import { User as UserModel, Post as PostModel } from 'generated/prisma';
13
14@Controller()
15export class AppController {
16  constructor(
17    private readonly userService: UsersService,
18    private readonly postService: PostsService,
19  ) {}
20
21  @Get('post/:id')
22  async getPostById(@Param('id') id: string): Promise<PostModel> {
23    return this.postService.post({ id: Number(id) });
24  }
25
26  @Get('feed')
27  async getPublishedPosts(): Promise<PostModel[]> {
28    return this.postService.posts({
29      where: { published: true },
30    });
31  }
32
33  @Get('filtered-posts/:searchString')
34  async getFilteredPosts(
35    @Param('searchString') searchString: string,
36  ): Promise<PostModel[]> {
37    return this.postService.posts({
38      where: {
39        OR: [
40          {
41            title: { contains: searchString },
42          },
43          {
44            content: { contains: searchString },
45          },
46        ],
47      },
48    });
49  }
50
51  @Post('post')
52  async createDraft(
53    @Body() postData: { title: string; content?: string; authorEmail: string },
54  ): Promise<PostModel> {
55    const { title, content, authorEmail } = postData;
56    return this.postService.createPost({
57      title,
58      content,
59      author: {
60        connect: { email: authorEmail },
61      },
62    });
63  }
64
65  @Post('user')
66  async signupUser(
67    @Body() userData: { name?: string; email: string },
68  ): Promise<UserModel> {
69    return this.userService.createUser(userData);
70  }
71
72  @Put('publish/:id')
73  async publishPost(@Param('id') id: string): Promise<PostModel> {
74    return this.postService.updatePost({
75      where: { id: Number(id) },
76      data: { published: true },
77    });
78  }
79
80  @Delete('post/:id')
81  async deletePost(@Param('id') id: string): Promise<PostModel> {
82    return this.postService.deletePost({ id: Number(id) });
83  }
84}

Ushbu controller quyidagi route'larni implement qiladi:

GET
  • /post/:id: id orqali bitta post'ni olish
  • /feed: Barcha published post'larni olish
  • /filter-posts/:searchString: title yoki content bo'yicha post'larni filter qilish
POST
  • /post: Yangi post yaratish
    • Body:
      • title: String (required): post sarlavhasi
      • content: String (optional): post matni
      • authorEmail: String (required): post yaratgan foydalanuvchining email'i
  • /user: Yangi foydalanuvchi yaratish
    • Body:
      • email: String (required): foydalanuvchi email manzili
      • name: String (optional): foydalanuvchi ismi
PUT
  • /publish/:id: id bo'yicha post'ni publish qilish
DELETE
  • /post/:id: id bo'yicha post'ni o'chirish

Xulosa

Ushbu recipe'da Prisma'ni NestJS bilan birga ishlatib REST API yaratishni o'rgandingiz. API route'larini implement qilgan controller PrismaService ga murojaat qiladi, u esa Prisma Client orqali database'ga query yuborib kiruvchi so'rovlarning ma'lumot ehtiyojini qoplaydi.

Agar NestJS bilan Prisma'dan foydalanish haqida ko'proq bilmoqchi bo'lsangiz, quyidagi resurslarni ko'ring:

  • NestJS & Prisma
  • Ready-to-run example projects for REST & GraphQL
  • Production-ready starter kit
  • Video: Accessing Databases using NestJS with Prisma (5min) by Marc Stammerjohann