Prisma ORM — первая программа
Prisma ORM — первая программа
Prisma — ORM (Object-Relational Mapping) для Node.js и TypeScript:
- схема в файле
schema.prisma; - миграции через CLI;
- типобезопасный Prisma Client с автодополнением в IDE.
Подходит к PostgreSQL, SQLite, MySQL и другим СУБД.
Предполагается, что REST API вы уже поднимали в Первая программа на Node.js или NestJS. Здесь — замена массива в памяти на реальную таблицу в базе данных.
| Шаг | Команда / файл | Результат |
|---|---|---|
| 1 | npm i prisma @prisma/client | Зависимости |
| 2 | npx prisma init | schema.prisma, .env |
| 3 | Модель Note | Описание таблицы |
| 4 | prisma migrate dev | SQL-миграция |
| 5 | PrismaClient в коде | CRUD с автодополнением |
| 6 | Express или NestJS | REST поверх БД |
| 7 | Studio и деплой | Просмотр данных и production |
| Материал | Зачем |
|---|---|
| Первая программа на Node.js | REST "Заметки", curl |
| Express — middleware | маршруты, JSON body |
| NestJS — первая программа | DI, сервис-слой |
| npm — команды и lock-файлы | install, scripts |
| SQL — о разделе | таблицы, SELECT, INSERT |
| TypeScript | типы Prisma Client |
Навигация по блоку Node.js
- REST без БД: Первая программа на Node.js
- Фреймворк: NestJS — первая программа
- Вы здесь: Prisma ORM — первая программа
- Альтернатива ORM: Drizzle ORM — первая программа
SQL вручную гибок, но в TypeScript-проекте легко ошибиться в именах колонок. Prisma генерирует типы из схемы — IDE подсказывает поля модели Note.
Что такое Prisma
ORM переводит операции с объектами (create, findMany) в SQL-запросы к СУБД. Prisma состоит из трёх частей:
| Компонент | Файл / команда | Роль |
|---|---|---|
| Schema | prisma/schema.prisma | Модели, связи, провайдер БД |
| Migrate | npx prisma migrate dev | Версионирование SQL |
| Client | @prisma/client | TypeScript API для запросов |
| Studio | npx 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 хранит базу в файле 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
}
Разбор полей:
| Поле | Prisma | SQL-смысл |
|---|---|---|
id | @id @default(autoincrement()) | PRIMARY KEY, автоинкремент |
text | String | TEXT 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 API | SQL-аналог |
|---|---|---|
| Список | 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 — сравнение на практике
| Критерий | Prisma | Drizzle |
|---|---|---|
| Схема | DSL schema.prisma | TypeScript-таблицы |
| Миграции | prisma migrate | drizzle-kit |
| Запросы | Client API | SQL-подобный query builder |
| Studio | встроенный | сторонние UI |
| Зрелость экосистемы | широкая | растёт быстро |
Сравнение на практике — Drizzle ORM — первая программа.
Упражнения
- Добавьте модель
Tagи связь many-to-many сNoteчерез промежуточную таблицу. - Напишите скрипт
npm run seedвpackage.jsonи добавьтеprisma/seed.tsв конфиг Prisma. - Добавьте поле
done Boolean @default(false)и эндпоинтPATCH /notes/:id/done. - Подключите
prisma.$transactionдля атомарного создания двух заметок. - Перенесите 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 дублирует данные | Нет очистки перед insert | deleteMany() в начале 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_URL | postgresql://... |
PORT | 8080 |
Секреты — только в env хостинга, не в git.
Бэкапы
PostgreSQL — регулярные дампы (pg_dump) или бэкапы провайдера. SQLite — копия файла .db.
Используйте 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 deploy | CI, 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 | Смысл | Действие |
|---|---|---|
| P2002 | Unique violation | Показать 409 Conflict |
| P2025 | Record not found | 404 |
| P2003 | Foreign key | Проверить связи |
| P1001 | Can't reach DB | Сеть, credentials |
| P1017 | Connection closed | Переподключение, pool |
| Симптом | Решение |
|---|---|
Migration failed to apply | Исправьте SQL, migrate resolve |
| Client устарел после pull | npx prisma generate |
| Studio не видит таблицы | Другой DATABASE_URL |
| Медленные запросы | EXPLAIN в PostgreSQL, индексы |
Дополнительные упражнения
- Добавьте пагинацию
?page=1&limit=10черезskipиtake. - Напишите фильтр
?q=текстчерезwhere: { text: { contains: q } }. - Экспортируйте данные в JSON через скрипт
prisma.note.findMany. - Настройте GitHub Action с
prisma migrate deployиnpm test. - Сравните тот же 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 dev | commit migration SQL |
| Переименование | @map или migration SQL | review diff |
| Seed обновлён | db seed | commit seed.ts |
| Prod deploy | migrate deploy | tag 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/ создан |
| seed | npx prisma db seed — строки в Studio |
| GET /notes | curl возвращает JSON |
| POST /notes | 201, запись в 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 | Убедиться в отсутствии ошибок в консоли |
Расширенные упражнения (второй проход)
- Many-to-many Note и Tag с промежуточной таблицей.
Подсказка к упражнению 13: Начните с минимального изменения, затем добавьте тест. Тема: Many-to-many.
- Seed 100 notes через Faker в seed.ts.
Подсказка к упражнению 14: Начните с минимального изменения, затем добавьте тест. Тема: Seed.
- PATCH done boolean с migrate dev.
Подсказка к упражнению 15: Начните с минимального изменения, затем добавьте тест. Тема: PATCH.
- Transaction: create two notes or rollback.
Подсказка к упражнению 16: Начните с минимального изменения, затем добавьте тест. Тема: Transaction:.
- Raw query: top 5 longest notes.
Подсказка к упражнению 17: Начните с минимального изменения, затем добавьте тест. Тема: Raw.
- Prisma middleware логирует duration ms.
Подсказка к упражнению 18: Начните с минимального изменения, затем добавьте тест. Тема: Prisma.
- Switch SQLite to PostgreSQL в Docker.
Подсказка к упражнению 19: Начните с минимального изменения, затем добавьте тест. Тема: Switch.
- NestJS PrismaModule global provider.
Подсказка к упражнению 20: Начните с минимального изменения, затем добавьте тест. Тема: NestJS.
- Handle P2002 unique violation в controller.
Подсказка к упражнению 21: Начните с минимального изменения, затем добавьте тест. Тема: Handle.
- 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 — дополнительные рекомендации
| # | Практика | Зачем |
|---|---|---|
| 1 | migrate | migrate deploy в CI, не migrate dev |
| 2 | PgBouncer | PgBouncer transaction mode с Prisma |
| 3 | Read | Read replicas через $extends read replica routing |
| 4 | Encrypt | Encrypt DATABASE_URL at rest in CI secrets |
| 5 | Monitor | Monitor slow queries через log: ['query'] sample rate |
| 6 | Backup | Backup pg_dump automated nightly |
| 7 | Staging | Staging DB refresh from anonymized prod |
| 8 | Version | Version lock @prisma/client with schema |
Troubleshooting — расширенная таблица
| Симптом | Вероятная причина | Действие |
|---|---|---|
| Сборка падает без текста | Кэш или версия Node | Очистить node_modules, lock-файл, переустановить |
| Тесты flaky | Порядок или timing | Изолировать example, убрать sleep, добавить wait matchers |
| Production 502 | Process не слушает PORT | Проверить env PORT и health endpoint |
| Данные пропали после deploy | In-memory store или migrate | Подключить БД, migrate deploy |
| CORS в браузере | Прямой URL API | Proxy dev или enableCors origin |
| Медленный первый запрос | Cold start DB pool | Warmup health check после deploy |
| Ошибка подписи iOS | Certificate expired | Renew в Developer portal, download profiles |
| Turbo frame blank | Id mismatch | Сверить turbo-frame id в request и response |
| Prisma client outdated | Schema changed | npx prisma generate после migrate |
| Vite blank prod | Неверный base path | Проверить base и URL деплоя |
Пошаговый walkthrough — контрольный список
День 1
- Шаг 1 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 1: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 2
- Шаг 1 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 2: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 3
- Шаг 1 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 3: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 4
- Шаг 1 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 4: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 5
- Шаг 1 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 5: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 6
- Шаг 1 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 5 дня 6: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
День 7
- Шаг 1 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 2 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 3 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 4 дня 7: закрепить часть стека Prisma ORM. Запишите результат в README проекта.
# checkpoint
npm test || bundle exec rspec || echo manual check
- Шаг 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 |
| Drizzle | Drizzle ORM — первая программа |
| SQL основы | SQL — о разделе |
| Node REST | Первая программа на Node.js |
| Express | Express — middleware, маршруты и ошибки |
| npm | npm — команды, зависимости и lock-файлы |
| TypeScript | Первая программа на TypeScript |
| Конфигурации | Конфигурации и данные — о разделе |