Перейти к основному содержимому

Prisma ORM — первая программа

Разработчику Архитектору

Prisma ORM — первая программа

PrismaORM (Object-Relational Mapping) для Node.js и TypeScript:

  • схема в файле schema.prisma;
  • миграции через CLI;
  • типобезопасный Prisma Client с автодополнением в IDE.

Подходит к PostgreSQL, SQLite, MySQL и другим СУБД.

Предполагается, что REST API вы уже поднимали в Первая программа на Node.js или NestJS. Здесь — замена массива в памяти на реальную таблицу в базе данных.

ШагКоманда / файлРезультат
1npm i prisma @prisma/clientЗависимости
2npx prisma initschema.prisma, .env
3Модель NoteОписание таблицы
4prisma migrate devSQL-миграция
5PrismaClient в кодеCRUD с автодополнением
6Express или NestJSREST поверх БД
7Studio и деплойПросмотр данных и production
МатериалЗачем
Первая программа на Node.jsREST "Заметки", curl
Express — middlewareмаршруты, JSON body
NestJS — первая программаDI, сервис-слой
npm — команды и lock-файлыinstall, scripts
SQL — о разделетаблицы, SELECT, INSERT
TypeScriptтипы Prisma Client

Навигация по блоку Node.js

Зачем ORM

SQL вручную гибок, но в TypeScript-проекте легко ошибиться в именах колонок. Prisma генерирует типы из схемы — IDE подсказывает поля модели Note.


Что такое Prisma

ORM переводит операции с объектами (create, findMany) в SQL-запросы к СУБД. Prisma состоит из трёх частей:

КомпонентФайл / командаРоль
Schemaprisma/schema.prismaМодели, связи, провайдер БД
Migratenpx prisma migrate devВерсионирование SQL
Client@prisma/clientTypeScript API для запросов
Studionpx prisma studioВеб-UI для таблиц

Шаг 1 — новый проект с нуля

Создайте каталог и инициализируйте npm (265):

mkdir notes-prisma
cd notes-prisma
npm init -y

Добавьте TypeScript (рекомендуется):

npm i -D typescript tsx @types/node
npx tsc --init

В package.json:

{
"name": "notes-prisma",
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"seed": "tsx src/seed.ts"
}
}

Шаг 2 — установка и инициализация Prisma

npm i prisma @prisma/client
npx prisma init --datasource-provider sqlite

Появятся:

notes-prisma/
prisma/
schema.prisma
.env
.gitignore

Файл .env:

DATABASE_URL="file:./dev.db"
SQLite для учёбы

SQLite хранит базу в файле dev.db — не нужен отдельный сервер PostgreSQL. Для production чаще берут PostgreSQL; строка подключения меняется в .env.


Шаг 3 — модель в schema.prisma

prisma/schema.prisma:

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}

model Note {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

Разбор полей:

ПолеPrismaSQL-смысл
id@id @default(autoincrement())PRIMARY KEY, автоинкремент
textStringTEXT NOT NULL
createdAt@default(now())дата создания
updatedAt@updatedAtобновляется при изменении

Применение схемы:

npx prisma migrate dev --name init_notes

Разбор:

  • migrate dev создаёт SQL в prisma/migrations/ и обновляет БД.
  • Генерируется клиент с типами TypeScript под вашу модель.
  • Имя миграции init_notes попадает в имя папки — удобно в git history.

Шаг 4 — PrismaClient и seed

src/db.ts:

import { PrismaClient } from '@prisma/client';

export const prisma = new PrismaClient({
log: ['query', 'error'],
});

src/seed.ts:

import { prisma } from './db.js';

async function main() {
await prisma.note.deleteMany();

await prisma.note.createMany({
data: [
{ text: 'Купить молоко' },
{ text: 'Прочитать про Prisma' },
],
});

const all = await prisma.note.findMany({ orderBy: { id: 'asc' } });
console.log(all);
}

main()
.catch(console.error)
.finally(() => prisma.$disconnect());

Запуск:

npm run seed

Разбор:

  • deleteMany() очищает таблицу перед seed — удобно при повторных запусках.
  • $disconnect() закрывает пул соединений; без этого процесс может висеть.
  • log: ['query'] печатает SQL в консоль — полезно при отладке.

Шаг 5 — CRUD через Prisma Client

ОперацияPrisma APISQL-аналог
СписокfindMany()SELECT * FROM Note
Одна записьfindUnique({ where: { id } })SELECT ... WHERE id = ?
Создатьcreate({ data: { text } })INSERT INTO ...
Обновитьupdate({ where, data })UPDATE ...
Удалитьdelete({ where: { id } })DELETE ...

Пример скрипта src/crud-demo.ts:

import { prisma } from './db.js';

async function demo() {
const created = await prisma.note.create({
data: { text: 'Демо CRUD' },
});
console.log('created', created);

const updated = await prisma.note.update({
where: { id: created.id },
data: { text: 'Текст обновлён' },
});
console.log('updated', updated);

await prisma.note.delete({ where: { id: created.id } });
console.log('deleted');
}

demo().finally(() => prisma.$disconnect());

Обработка "не найдено":

import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

try {
await prisma.note.delete({ where: { id: 999 } });
} catch (e) {
if (e instanceof PrismaClientKnownRequestError && e.code === 'P2025') {
console.log('Запись не найдена');
} else {
throw e;
}
}

Шаг 6 — Express + Prisma (полный walkthrough)

Установите Express:

npm i express
npm i -D @types/express

src/server.ts:

import express from 'express';
import { prisma } from './db.js';

const app = express();
app.use(express.json());

app.get('/health', (_req, res) => {
res.json({ status: 'ok' });
});

app.get('/notes', async (_req, res) => {
const notes = await prisma.note.findMany({ orderBy: { id: 'desc' } });
res.json(notes);
});

app.get('/notes/:id', async (req, res) => {
const id = Number(req.params.id);
const note = await prisma.note.findUnique({ where: { id } });
if (!note) return res.status(404).json({ error: 'not found' });
res.json(note);
});

app.post('/notes', async (req, res) => {
const text = String(req.body?.text ?? '').trim();
if (!text) return res.status(400).json({ error: 'text required' });
const note = await prisma.note.create({ data: { text } });
res.status(201).json(note);
});

app.delete('/notes/:id', async (req, res) => {
const id = Number(req.params.id);
try {
await prisma.note.delete({ where: { id } });
res.status(204).end();
} catch {
res.status(404).json({ error: 'not found' });
}
});

const PORT = process.env.PORT ?? 3000;
app.listen(PORT, () => console.log(`http://localhost:${PORT}`));

Проверка — те же curl, что в 262:

npm run dev
curl http://127.0.0.1:3000/notes
curl -X POST http://127.0.0.1:3000/notes \
-H "Content-Type: application/json" \
-d '{"text":"Изучить Prisma"}'

Перезапустите сервер — данные сохранятся в dev.db. Это главное отличие от in-memory store.

CORS для фронтенда — Fullstack на JavaScript — API и фронтенд.


Шаг 7 — NestJS + Prisma

Сервис-подключение к БД:

src/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();
}

async onModuleDestroy() {
await this.$disconnect();
}
}

src/notes/notes.service.ts:

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class NotesService {
constructor(private readonly prisma: PrismaService) {}

findAll() {
return this.prisma.note.findMany({ orderBy: { id: 'desc' } });
}

async create(text: string) {
return this.prisma.note.create({ data: { text } });
}

async remove(id: number) {
try {
await this.prisma.note.delete({ where: { id } });
} catch {
throw new NotFoundException(`Note ${id} not found`);
}
}
}

В NotesModule:

@Module({
controllers: [NotesController],
providers: [NotesService, PrismaService],
})
export class NotesModule {}

Подробнее о NestJS — Первая программа на NestJS.


Prisma Studio

Визуальный просмотр данных:

npx prisma studio

Откроется http://localhost:5555 — таблицы как в простом админ-UI. Удобно проверять seed и отладку без SQL-клиента.


PostgreSQL вместо SQLite

Измените .env:

DATABASE_URL="postgresql://user:pass@localhost:5432/notes?schema=public"

В schema.prisma:

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

Новая миграция:

npx prisma migrate dev --name switch_to_pg

На хостинге (Railway, Render, Supabase) строку DATABASE_URL задают в панели окружения.


Prisma и Drizzle — сравнение на практике

КритерийPrismaDrizzle
СхемаDSL schema.prismaTypeScript-таблицы
Миграцииprisma migratedrizzle-kit
ЗапросыClient APISQL-подобный query builder
Studioвстроенныйсторонние UI
Зрелость экосистемыширокаярастёт быстро

Сравнение на практике — Drizzle ORM — первая программа.


Упражнения

  1. Добавьте модель Tag и связь many-to-many с Note через промежуточную таблицу.
  2. Напишите скрипт npm run seed в package.json и добавьте prisma/seed.ts в конфиг Prisma.
  3. Добавьте поле done Boolean @default(false) и эндпоинт PATCH /notes/:id/done.
  4. Подключите prisma.$transaction для атомарного создания двух заметок.
  5. Перенесите Express API в NestJS с PrismaService.

Частые ошибки и troubleshooting

СимптомПричинаРешение
Environment variable not found: DATABASE_URLНет .env или неверный путьФайл .env в корне проекта
Типы не обновились после schemaКлиент не перегенерированnpx prisma generate
Unique constraint failedДубликат уникального поляОбработайте код P2002 в коде
The table does not existМиграция не примененаnpx prisma migrate dev
Процесс не завершаетсяНет $disconnect().finally(() => prisma.$disconnect())
Invalid prisma.note.findMany()Опечатка в имени моделиИмена моделей в camelCase в Client
SQLite lockedДва процесса пишут в файлОдин writer или PostgreSQL
Seed дублирует данныеНет очистки перед insertdeleteMany() в начале seed

FAQ

Нужно ли знать SQL для Prisma?

Базовое понимание таблиц и SELECT помогает (SQL — о разделе). Prisma скрывает строки SQL, но миграции — это SQL-файлы.

Где хранить dev.db?

Локально в проекте; добавьте в .gitignore. На production SQLite редко используют — берут PostgreSQL.

Как обновить схему без потери данных?

Меняете schema.prisma, затем npx prisma migrate dev --name описание. Prisma создаст ALTER-миграцию.

Prisma или Drizzle?

Prisma — быстрый старт и Studio. Drizzle — если хотите SQL-подобные запросы в TypeScript (2692).


Production и деплой

Миграции на сервере

В CI/CD и на хостинге:

npx prisma migrate deploy
npx prisma generate
npm run build
npm run start:prod

migrate deploy применяет pending-миграции без интерактива (в отличие от migrate dev).

Переменные окружения

ПеременнаяПример
DATABASE_URLpostgresql://...
PORT8080

Секреты — только в env хостинга, не в git.

Бэкапы

PostgreSQL — регулярные дампы (pg_dump) или бэкапы провайдера. SQLite — копия файла .db.

Production

Используйте connection pooling (PgBouncer или встроенный у провайдера). Не коммитьте .env с паролями. Проверяйте миграции на staging перед production.


Шаг 8 — relations и связанные модели

Добавим теги к заметкам. Обновите schema.prisma:

model Note {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tags TagOnNote[]
}

model Tag {
id Int @id @default(autoincrement())
name String @unique
notes TagOnNote[]
}

model TagOnNote {
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
noteId Int
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
tagId Int

@@id([noteId, tagId])
}
npx prisma migrate dev --name add_tags

Запрос с include:

const notes = await prisma.note.findMany({
include: {
tags: {
include: { tag: true },
},
},
});

Prisma генерирует вложенные типы — IDE подсказывает include.


Шаг 9 — seed через официальный механизм

package.json:

{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}

prisma/seed.ts:

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
await prisma.note.deleteMany();
await prisma.tag.deleteMany();

const work = await prisma.tag.create({ data: { name: 'work' } });
const home = await prisma.tag.create({ data: { name: 'home' } });

await prisma.note.create({
data: {
text: 'Сделать отчёт',
tags: {
create: [{ tagId: work.id }],
},
},
});

await prisma.note.create({
data: {
text: 'Купить молоко',
tags: {
create: [{ tagId: home.id }],
},
},
});
}

main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
npx prisma db seed

Шаг 10 — транзакции

Атомарное создание нескольких записей:

await prisma.$transaction(async (tx) => {
const note = await tx.note.create({ data: { text: 'Пакетная заметка' } });
await tx.tagOnNote.create({
data: { noteId: note.id, tagId: 1 },
});
return note;
});

Если второй запрос упадёт — первый откатится. Для финансовых операций это критично.


Шаг 11 — raw SQL когда нужна гибкость

const rows = await prisma.$queryRaw<{ count: bigint }[]>`
SELECT COUNT(*) as count FROM Note
`;

Или typed:

import { Prisma } from '@prisma/client';

const result = await prisma.$queryRaw(
Prisma.sql`SELECT * FROM Note WHERE text LIKE ${'%молоко%'}`
);

Raw SQL — escape hatch; для 90% задач хватит Client API.


Шаг 12 — Express middleware stack с Prisma

Полный src/server.ts с обработкой ошибок:

import express from 'express';
import { prisma } from './db.js';
import { Prisma } from '@prisma/client';

const app = express();
app.use(express.json());

app.use((req, _res, next) => {
console.log(req.method, req.url);
next();
});

// ... маршруты notes ...

app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
if (err instanceof Prisma.PrismaClientKnownRequestError) {
if (err.code === 'P2025') {
return res.status(404).json({ error: 'not found' });
}
}
console.error(err);
res.status(500).json({ error: 'internal' });
});

process.on('SIGINT', async () => {
await prisma.$disconnect();
process.exit(0);
});

Graceful shutdown закрывает пул соединений — важно для production.


Миграции — жизненный цикл

КомандаГде
migrate devЛокальная разработка
migrate deployCI, staging, production
migrate resetТолько dev — удаляет все данные
db pushПрототип без SQL-файлов

На production никогда не используйте migrate reset.


NestJS + Prisma — полный NotesService

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateNoteDto } from './dto/create-note.dto';

@Injectable()
export class NotesService {
constructor(private readonly prisma: PrismaService) {}

findAll() {
return this.prisma.note.findMany({
orderBy: { id: 'desc' },
include: { tags: { include: { tag: true } } },
});
}

async findOne(id: number) {
const note = await this.prisma.note.findUnique({ where: { id } });
if (!note) throw new NotFoundException();
return note;
}

create(dto: CreateNoteDto) {
return this.prisma.note.create({ data: { text: dto.text } });
}

async remove(id: number) {
try {
await this.prisma.note.delete({ where: { id } });
} catch {
throw new NotFoundException();
}
}
}

PrismaModule экспортирует PrismaService — см. NestJS.


Docker Compose с PostgreSQL

docker-compose.yml:

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: notes
POSTGRES_PASSWORD: notes
POSTGRES_DB: notes
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data

volumes:
pgdata:

.env:

DATABASE_URL="postgresql://notes:notes@localhost:5432/notes?schema=public"
docker compose up -d
npx prisma migrate dev
npm run dev

Расширенный troubleshooting

Код PrismaСмыслДействие
P2002Unique violationПоказать 409 Conflict
P2025Record not found404
P2003Foreign keyПроверить связи
P1001Can't reach DBСеть, credentials
P1017Connection closedПереподключение, pool
СимптомРешение
Migration failed to applyИсправьте SQL, migrate resolve
Client устарел после pullnpx prisma generate
Studio не видит таблицыДругой DATABASE_URL
Медленные запросыEXPLAIN в PostgreSQL, индексы

Дополнительные упражнения

  1. Добавьте пагинацию ?page=1&limit=10 через skip и take.
  2. Напишите фильтр ?q=текст через where: { text: { contains: q } }.
  3. Экспортируйте данные в JSON через скрипт prisma.note.findMany.
  4. Настройте GitHub Action с prisma migrate deploy и npm test.
  5. Сравните тот же CRUD на Drizzle — таблица отличий в README.

Расширенный FAQ

Prisma Accelerate / Pulse?

Облачные продукты Prisma для connection pooling и realtime — опционально для production.

Как откатить миграцию?

Создайте новую миграцию с обратными изменениями. Откат файлов миграций в git опасен на prod.

Prisma с MongoDB?

Поддерживается отдельным provider — другая модель без JOIN.

N+1 problem?

Используйте include или один запрос с select. Prisma имеет dataloader-паттерны в документации.


Шаг 13 — пагинация и фильтрация в Express

app.get('/notes', async (req, res) => {
const page = Math.max(1, Number(req.query.page) || 1);
const limit = Math.min(100, Number(req.query.limit) || 20);
const q = String(req.query.q ?? '').trim();

const where = q
? { text: { contains: q, mode: 'insensitive' as const } }
: undefined;

const [items, total] = await Promise.all([
prisma.note.findMany({
where,
orderBy: { id: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
prisma.note.count({ where }),
]);

res.json({ items, total, page, limit });
});

Для SQLite mode: 'insensitive' недоступен — используйте contains без mode или PostgreSQL.

Проверка:

curl "http://127.0.0.1:3000/notes?page=1&limit=5&q=молоко"

Шаг 14 — интеграционные тесты API

npm i -D vitest supertest @types/supertest

src/server.test.ts:

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import express from 'express';
// импорт app factory или export app из server.ts

describe('Notes API', () => {
it('GET /health', async () => {
const res = await request(app).get('/health');
expect(res.status).toBe(200);
});
});

Запуск: npx vitest run. Тесты с реальной БД — отдельная DATABASE_URL для test.sqlite.


Шаг 15 — схема и типы в IDE

После prisma generate тип Note доступен:

import type { Note } from '@prisma/client';

function formatNote(note: Note): string {
return `[${note.id}] ${note.text}`;
}

При изменении schema IDE покажет ошибки там, где поля устарели — главная ценность Prisma в TypeScript-проектах (TypeScript).


Журнал изменений schema — практика

ДействиеКомандаGit
Новое полеправка schema → migrate devcommit migration SQL
Переименование@map или migration SQLreview diff
Seed обновлёнdb seedcommit seed.ts
Prod deploymigrate deploytag release

Никогда не редактируйте уже применённые migration SQL на production — только новая миграция.


Полный практикум — REST "Заметки" на Prisma за один проход

Ниже — последовательность команд без пропусков. Каталог notes-prisma-full.

mkdir notes-prisma-full && cd notes-prisma-full
npm init -y
npm pkg set type=module
npm i express prisma @prisma/client
npm i -D typescript tsx @types/express @types/node
npx tsc --init
npx prisma init --datasource-provider sqlite

Скопируйте модель Note из шага 3, затем:

npx prisma migrate dev --name init

Создайте src/db.ts, src/server.ts (Express из шага 6), package.json scripts:

{
"scripts": {
"dev": "tsx watch src/server.ts",
"db:studio": "prisma studio"
}
}

Терминал 1: npm run dev. Терминал 2: проверка curl из 262. Терминал 3 (опционально): npm run db:studio.

Следующий шаг — перенос на NestJS: скопируйте prisma/ в Nest-проект, добавьте PrismaService, замените массив в NotesService.

ЭтапПроверка
migrate devФайл prisma/migrations/ создан
seednpx prisma db seed — строки в Studio
GET /notescurl возвращает JSON
POST /notes201, запись в Studio
restart serverДанные на месте


Второй проход — расширенный практикум (Prisma ORM)

Серия мини-туториалов

Туториал 1 — Relations

Команда или API: model Tag + NoteTag[].

Детали: include: { tags: true } в findMany.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Relations
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 2 — Transactions

Команда или API: prisma.$transaction.

Детали: atomic create note + tag.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Transactions
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 3 — Raw SQL

Команда или API: prisma.$queryRaw.

Детали: SELECT COUNT(*) для отчётов.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Raw SQL
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 4 — Middleware

Команда или API: prisma.$use.

Детали: логирование всех queries.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Middleware
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 5 — Seed config

Команда или API: package.json prisma.seed.

Детали: tsx prisma/seed.ts.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Seed config
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 6 — Enum field

Команда или API: enum Status.

Детали: draft | published в schema.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Enum field
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 7 — Soft delete

Команда или API: deletedAt DateTime?.

Детали: findMany where deletedAt null.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Soft delete
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 8 — Full-text

Команда или API: PostgreSQL tsvector.

Детали: raw search по text.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Full-text
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 9 — Extensions

Команда или API: previewFeatures.

Детали: postgresqlExtensions при необходимости.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Extensions
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Туториал 10 — Accelerate

Команда или API: Prisma Accelerate.

Детали: connection pooling edge.

// пример шага
console.log('ok');
ШагПроверка
1Выполнить Accelerate
2Перезапустить dev-сервер
3Убедиться в отсутствии ошибок в консоли

Расширенные упражнения (второй проход)

  1. Many-to-many Note и Tag с промежуточной таблицей.

Подсказка к упражнению 13: Начните с минимального изменения, затем добавьте тест. Тема: Many-to-many.

  1. Seed 100 notes через Faker в seed.ts.

Подсказка к упражнению 14: Начните с минимального изменения, затем добавьте тест. Тема: Seed.

  1. PATCH done boolean с migrate dev.

Подсказка к упражнению 15: Начните с минимального изменения, затем добавьте тест. Тема: PATCH.

  1. Transaction: create two notes or rollback.

Подсказка к упражнению 16: Начните с минимального изменения, затем добавьте тест. Тема: Transaction:.

  1. Raw query: top 5 longest notes.

Подсказка к упражнению 17: Начните с минимального изменения, затем добавьте тест. Тема: Raw.

  1. Prisma middleware логирует duration ms.

Подсказка к упражнению 18: Начните с минимального изменения, затем добавьте тест. Тема: Prisma.

  1. Switch SQLite to PostgreSQL в Docker.

Подсказка к упражнению 19: Начните с минимального изменения, затем добавьте тест. Тема: Switch.

  1. NestJS PrismaModule global provider.

Подсказка к упражнению 20: Начните с минимального изменения, затем добавьте тест. Тема: NestJS.

  1. Handle P2002 unique violation в controller.

Подсказка к упражнению 21: Начните с минимального изменения, затем добавьте тест. Тема: Handle.

  1. Export schema ERD через prisma-erd-generator.

Подсказка к упражнению 22: Начните с минимального изменения, затем добавьте тест. Тема: Export.


Расширенный FAQ (второй проход)

Migrate reset dev?

prisma migrate reset — удалит данные dev.

Schema drift?

prisma db pull если БД изменена вручную.

Binary targets?

generator binaryTargets для Docker alpine.

Connection limit?

connection_limit в DATABASE_URL + pooler.

Prisma + Edge?

Driver adapters, не classic engine в Workers.

Seeding production?

Только idempotent seed, не deleteMany.

JSON field?

Json type в schema, Prisma.JsonValue в TS.

Cascade delete?

onDelete: Cascade на relation.

Introspect legacy DB?

prisma db pull + migrate diff.

Studio production?

Не открывайте Studio на prod DB публично.


Production — дополнительные рекомендации

#ПрактикаЗачем
1migratemigrate deploy в CI, не migrate dev
2PgBouncerPgBouncer transaction mode с Prisma
3ReadRead replicas через $extends read replica routing
4EncryptEncrypt DATABASE_URL at rest in CI secrets
5MonitorMonitor slow queries через log: ['query'] sample rate
6BackupBackup pg_dump automated nightly
7StagingStaging DB refresh from anonymized prod
8VersionVersion lock @prisma/client with schema

Troubleshooting — расширенная таблица

СимптомВероятная причинаДействие
Сборка падает без текстаКэш или версия NodeОчистить node_modules, lock-файл, переустановить
Тесты flakyПорядок или timingИзолировать example, убрать sleep, добавить wait matchers
Production 502Process не слушает PORTПроверить env PORT и health endpoint
Данные пропали после deployIn-memory store или migrateПодключить БД, migrate deploy
CORS в браузереПрямой URL APIProxy dev или enableCors origin
Медленный первый запросCold start DB poolWarmup health check после deploy
Ошибка подписи iOSCertificate expiredRenew в Developer portal, download profiles
Turbo frame blankId mismatchСверить turbo-frame id в request и response
Prisma client outdatedSchema changednpx prisma generate после migrate
Vite blank prodНеверный base pathПроверить base и URL деплоя

Пошаговый walkthrough — контрольный список

День 1

  1. Шаг 1 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 2

  1. Шаг 1 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 3

  1. Шаг 1 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 4

  1. Шаг 1 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 5

  1. Шаг 1 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 6

  1. Шаг 1 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

День 7

  1. Шаг 1 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 2 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 3 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 4 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
  1. Шаг 5 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check

Чек-лист самопроверки перед сдачей практикума

  • Проект создаётся с нуля по статье без пропусков шагов

  • CRUD или эквивалентный сценарий работает end-to-end

  • Есть обработка ошибок валидации или 404

  • Данные переживают перезапуск там, где это требуется темой

  • Написан минимум один автоматический тест или system check

  • Production-секция прочитана и применена к деплою или Docker

  • FAQ просмотрен — типичные ошибки воспроизведены и исправлены

  • Связанные материалы открыты для следующего шага обучения

Связанные материалы

ТемаМатериал
NestJSПервая программа на NestJS
DrizzleDrizzle ORM — первая программа
SQL основыSQL — о разделе
Node RESTПервая программа на Node.js
ExpressExpress — middleware, маршруты и ошибки
npmnpm — команды, зависимости и lock-файлы
TypeScriptПервая программа на TypeScript
КонфигурацииКонфигурации и данные — о разделе

Содержание