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

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

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

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

Drizzle ORM — компактный ORM для TypeScript:

  • схема таблиц пишется на TypeScript;
  • запросы близки к SQL;
  • миграции через drizzle-kit.

Отдельного DSL-файла вроде schema.prisma у Prisma нет — всё живёт в коде.

Подходит, если хотите полный контроль над SQL и минимальный runtime overhead.

ШагДействиеРезультат
1npm init, зависимостиКаркас проекта
2src/schema.tsОписание таблицы
3drizzle-kit generateSQL-миграция
4drizzle-kit migrateФайл dev.db
5CRUD в seed.tsПроверка запросов
6Express RESTAPI "Заметки"
7PostgreSQLПереход на production-СУБД
МатериалЗачем
Prisma ORM — первая программаальтернатива с schema.prisma
Первая программа на Node.jsREST, curl
Express — middlewareмаршруты
NestJSсервис-слой
npmscripts, devDependencies
TypeScriptсхема и запросы

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

Кому подходит Drizzle

Если вы уже comfortable с SQL и хотите видеть структуру запросов в TypeScript — Drizzle даёт SQL-подобный синтаксис без отдельного языка схемы.


Архитектура Drizzle

ПакетНазначение
drizzle-ormQuery builder в runtime
drizzle-kitCLI миграций (devDependency)
better-sqlite3Драйвер SQLite для Node
tsxЗапуск TypeScript без сборки

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

mkdir notes-drizzle
cd notes-drizzle
npm init -y

package.json:

{
"name": "notes-drizzle",
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"seed": "tsx src/seed.ts",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate"
}
}

Установка:

npm i drizzle-orm better-sqlite3 express
npm i -D drizzle-kit tsx typescript @types/better-sqlite3 @types/express @types/node
npx tsc --init

Шаг 2 — конфиг drizzle-kit

drizzle.config.ts:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
schema: './src/schema.ts',
out: './drizzle',
dialect: 'sqlite',
dbCredentials: { url: './dev.db' },
});

Разбор:

  • schema — путь к TypeScript-описанию таблиц.
  • out — каталог SQL-миграций.
  • dialect — тип СУБД (sqlite, postgresql, mysql).

Шаг 3 — схема на TypeScript

src/schema.ts:

import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';

export const notes = sqliteTable('notes', {
id: integer('id').primaryKey({ autoIncrement: true }),
text: text('text').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.$defaultFn(() => new Date()),
});

Разбор:

МетодСмысл
sqliteTable('notes', {...})Имя таблицы в БД
integer(...).primaryKey({ autoIncrement: true })AUTOINCREMENT PK
text('text').notNull()NOT NULL колонка
mode: 'timestamp'JavaScript Date ↔ integer в SQLite

Экспорт notes используется и в миграциях, и в запросах — один источник правды.


Шаг 4 — миграции

npm run db:generate
npm run db:migrate

После generate в drizzle/ появится SQL-файл с CREATE TABLE. migrate применяет его к dev.db.

Порядок команд

Сначала меняете schema.ts, затем generate, затем migrate. Пропуск migrate даёт ошибку "no such table".


Шаг 5 — подключение и seed

src/db.ts:

import Database from 'better-sqlite3';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import * as schema from './schema.js';

const sqlite = new Database('dev.db');
export const db = drizzle(sqlite, { schema });

src/seed.ts:

import { db } from './db.js';
import { notes } from './schema.js';
import { desc } from 'drizzle-orm';

async function main() {
await db.delete(notes);

await db.insert(notes).values([
{ text: 'Первая заметка Drizzle' },
{ text: 'Вторая заметка' },
]);

const rows = await db.select().from(notes).orderBy(desc(notes.id));
console.log(rows);
}

main();
npm run seed

Шаг 6 — CRUD-операции

ЗадачаDrizzle
SELECT всеdb.select().from(notes)
WHERE iddb.select().from(notes).where(eq(notes.id, id))
INSERTdb.insert(notes).values({ text })
INSERT + return.returning()
DELETEdb.delete(notes).where(eq(notes.id, id))
UPDATEdb.update(notes).set({ text }).where(eq(notes.id, id))

Пример src/queries-demo.ts:

import { db } from './db.js';
import { notes } from './schema.js';
import { eq } from 'drizzle-orm';

const [row] = await db
.insert(notes)
.values({ text: 'Demo' })
.returning();

await db
.update(notes)
.set({ text: 'Updated' })
.where(eq(notes.id, row.id));

await db.delete(notes).where(eq(notes.id, row.id));

.returning() возвращает созданную строку — удобно для REST 201 Created.


Шаг 7 — Express REST API (полный walkthrough)

src/server.ts:

import express from 'express';
import { db } from './db.js';
import { notes } from './schema.js';
import { desc, eq } from 'drizzle-orm';

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

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

app.get('/notes', async (_req, res) => {
const rows = await db.select().from(notes).orderBy(desc(notes.id));
res.json(rows);
});

app.get('/notes/:id', async (req, res) => {
const id = Number(req.params.id);
const [row] = await db.select().from(notes).where(eq(notes.id, id));
if (!row) return res.status(404).json({ error: 'not found' });
res.json(row);
});

app.post('/notes', async (req, res) => {
const text = String(req.body?.text ?? '').trim();
if (!text) return res.status(400).json({ error: 'text required' });
const [row] = await db.insert(notes).values({ text }).returning();
res.status(201).json(row);
});

app.delete('/notes/:id', async (req, res) => {
const id = Number(req.params.id);
const deleted = await db.delete(notes).where(eq(notes.id, id)).returning();
if (deleted.length === 0) return res.status(404).json({ error: 'not found' });
res.status(204).end();
});

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

Проверка (как в 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":"Drizzle работает"}'

.returning() поддерживается в SQLite и PostgreSQL; для MySQL проверьте версию СУБД.


Шаг 8 — NestJS + Drizzle

src/database/database.module.ts:

import { Module, Global } from '@nestjs/common';
import { db } from '../db.js';

export const DRIZZLE = Symbol('DRIZZLE');

@Global()
@Module({
providers: [{ provide: DRIZZLE, useValue: db }],
exports: [DRIZZLE],
})
export class DatabaseModule {}

В сервисе:

import { Inject, Injectable } from '@nestjs/common';
import { DRIZZLE } from '../database/database.module';
import { notes } from '../schema.js';
import { desc } from 'drizzle-orm';

@Injectable()
export class NotesService {
constructor(@Inject(DRIZZLE) private readonly db: typeof import('./db.js').db) {}

findAll() {
return this.db.select().from(notes).orderBy(desc(notes.id));
}
}

Подробнее о NestJS — 269.


PostgreSQL вместо SQLite

npm i drizzle-orm postgres
npm i -D drizzle-kit

src/schema.tspgTable из drizzle-orm/pg-core:

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

export const notes = pgTable('notes', {
id: serial('id').primaryKey(),
text: text('text').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});

drizzle.config.ts:

export default defineConfig({
schema: './src/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL! },
});

Подключение:

import postgres from 'postgres';
import { drizzle } from 'drizzle-orm/postgres-js';

const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });

Drizzle и Prisma — когда что выбирать

Выбирайте DrizzleВыбирайте Prisma
SQL-подобный стиль запросовЕдиный schema.prisma и Studio
Минимум магии, tree-shakingБогатые relation API
Edge/serverless (drizzle + libsql)Команда уже на Prisma
Схема рядом с кодомВизуальный Studio из коробки

Обе ORM сочетаются с NestJS через сервис-слой.


Упражнения

  1. Добавьте колонку done (integer 0/1) и эндпоинт PATCH /notes/:id/toggle.
  2. Напишите join двух таблиц notes и categories через leftJoin.
  3. Оберните insert+update в db.transaction().
  4. Сравните тот же CRUD на Prisma — запишите отличия в комментарии.
  5. Подключите фронтенд Svelte через прокси Vite.

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

СимптомПричинаРешение
no such table: notesМиграция не примененаnpm run db:migrate
Тип Date странный в SQLiteБез mode: 'timestamp'Укажите mode в schema
returning() пустойDialect без поддержкиПроверьте SQLite/PG версию
Cannot find module './schema.js'ESM paths"type": "module" и .js в import
Дубликаты при seedНет delete перед insertawait db.delete(notes)
drizzle-kit not founddevDependencynpm i -D drizzle-kit
Locked SQLite DBДва процессаОдин npm run dev

FAQ

Drizzle заменяет SQL?

Нет. Drizzle генерирует SQL под капотом. Сложные отчёты иногда пишут через db.execute(sql...).

Нужен ли отдельный schema-файл?

Схема — обычный TypeScript. Это плюс для тех, кто хочет всё в репозитории без DSL.

Есть ли GUI как Prisma Studio?

Официального Studio нет; используйте DB Browser for SQLite, TablePlus или pgAdmin.

Drizzle в serverless?

Да, с @libsql/client и Turso — популярная связка для edge.


Production и деплой

Миграции в CI

npm run db:migrate
npm run build
node dist/server.js

Храните SQL-миграции в git. На production — drizzle-kit migrate, не generate.

Connection pooling

Для PostgreSQL используйте postgres.js с лимитом соединений или PgBouncer. SQLite на production — редко; для нагрузки — PostgreSQL.

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

DATABASE_URL=postgresql://user:pass@host:5432/db
PORT=8080
Production

Коммитьте миграции, не dev.db. Бэкапы PostgreSQL — через провайдера или pg_dump.


Шаг 9 — relations в Drizzle

src/schema.ts с тегами:

import { sqliteTable, integer, text, primaryKey } from 'drizzle-orm/sqlite-core';
import { relations } from 'drizzle-orm';

export const notes = sqliteTable('notes', {
id: integer('id').primaryKey({ autoIncrement: true }),
text: text('text').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
});

export const tags = sqliteTable('tags', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull().unique(),
});

export const notesToTags = sqliteTable(
'notes_to_tags',
{
noteId: integer('note_id').notNull().references(() => notes.id, { onDelete: 'cascade' }),
tagId: integer('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
},
(t) => [primaryKey({ columns: [t.noteId, t.tagId] })],
);

export const notesRelations = relations(notes, ({ many }) => ({
notesToTags: many(notesToTags),
}));

После изменений: npm run db:generate && npm run db:migrate.

Join-запрос:

import { eq } from 'drizzle-orm';

const rows = await db
.select({
noteId: notes.id,
text: notes.text,
tagName: tags.name,
})
.from(notes)
.leftJoin(notesToTags, eq(notes.id, notesToTags.noteId))
.leftJoin(tags, eq(notesToTags.tagId, tags.id));

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

await db.transaction(async (tx) => {
const [note] = await tx.insert(notes).values({ text: 'В транзакции' }).returning();
await tx.insert(tags).values({ name: `tag-for-${note.id}` });
});

При ошибке во втором insert первый откатится.


Шаг 11 — prepared statements и производительность

better-sqlite3 синхронный; для PostgreSQL используйте postgres.js:

import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

const client = postgres(process.env.DATABASE_URL!, { max: 10 });
export const db = drizzle(client, { schema });

Параметр max — размер пула соединений.


Шаг 12 — drizzle-kit studio (просмотр данных)

npx drizzle-kit studio

Откроется UI для просмотра таблиц — аналог Prisma Studio (2691).


Полный Express с error handler

import express from 'express';
import { db } from './db.js';
import { notes } from './schema.js';
import { desc, eq } from 'drizzle-orm';

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

app.get('/notes', async (_req, res, next) => {
try {
const rows = await db.select().from(notes).orderBy(desc(notes.id));
res.json(rows);
} catch (e) {
next(e);
}
});

app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(err);
res.status(500).json({ error: 'internal' });
});

const server = app.listen(3000);

process.on('SIGINT', () => {
server.close();
process.exit(0);
});

Сравнение запросов Prisma и Drizzle

ЗадачаPrismaDrizzle
Все заметкиprisma.note.findMany()db.select().from(notes)
WHERE idfindUnique({ where: { id } }).where(eq(notes.id, id))
INSERTcreate({ data })insert(notes).values({})
JOINinclude: { tags: true }.leftJoin(...)
Raw SQL$queryRawdb.execute(sql...)

Docker и PostgreSQL

docker-compose.yml — тот же паттерн, что в Prisma. Обновите drizzle.config.ts:

export default defineConfig({
schema: './src/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL! },
});

Entrypoint скрипт:

npm run db:migrate && npm run dev

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

СимптомДетали
SQLITE_BUSYДва writer — один процесс или PG
Migration driftdrizzle-kit push только dev
Type inference сломанаtypeof notes.$inferSelect
Import schema circularВынесите relations отдельно

Тип inferred row:

type Note = typeof notes.$inferSelect;

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

  1. Пагинация через .limit(10).offset(20).
  2. Поиск LIKE через like(notes.text, '%молоко%').
  3. Unit-тест с in-memory libsql (Drizzle + Turso).
  4. Экспорт schema в SQL через drizzle-kit export.
  5. Интеграция с NestJS Guards — проверка JWT перед CRUD.

Шаг 13 — пагинация

app.get('/notes', async (req, res) => {
const page = Number(req.query.page) || 1;
const limit = Number(req.query.limit) || 20;
const offset = (page - 1) * limit;

const rows = await db
.select()
.from(notes)
.orderBy(desc(notes.id))
.limit(limit)
.offset(offset);

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

Шаг 14 — индексы в schema

import { index } from 'drizzle-orm/sqlite-core';

export const notes = sqliteTable(
'notes',
{
id: integer('id').primaryKey({ autoIncrement: true }),
text: text('text').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
},
(table) => [index('notes_text_idx').on(table.text)],
);

После generate + migrate поиск по text ускорится на больших таблицах.


Шаг 15 — тесты с vitest

import { describe, it, expect, beforeEach } from 'vitest';
import Database from 'better-sqlite3';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import { notes } from './schema';

describe('notes table', () => {
let db: ReturnType<typeof drizzle>;

beforeEach(() => {
const sqlite = new Database(':memory:');
db = drizzle(sqlite);
// apply migration SQL to memory db
});

it('inserts row', async () => {
await db.insert(notes).values({ text: 'test' });
const rows = await db.select().from(notes);
expect(rows).toHaveLength(1);
});
});

In-memory SQLite — быстрые тесты без файла dev.db.


Миграция с Prisma на Drizzle (концептуально)

PrismaDrizzle
schema.prismaschema.ts
prisma migratedrizzle-kit generate/migrate
PrismaClientdrizzle(db)
Studiodrizzle-kit studio

Данные сохраняются в SQL — меняется только слой доступа. Подробное сравнение — 2691.


Production checklist

ПунктДействие
Миграции в gitdrizzle/*.sql
CI migratedrizzle-kit migrate
Pool sizeНастройка драйвера
Индексы.unique(), custom indexes
Логи SQLlogger: true в drizzle config (dev only)


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

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

Туториал 1 — Relations

Команда или API: relations() helper.

Детали: db.query.notes.findMany({ with: { tags: true } }).

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

Туториал 2 — Transactions

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

Детали: async tx => tx.insert.

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

Туториал 3 — Prepared statements

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

Детали: performance repeated queries.

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

Туториал 4 — Views

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

Детали: materialized reporting view.

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

Туториал 5 — Indexes

Команда или API: index on text.

Детали: migration add index.

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

Туториал 6 — Zod schema

Команда или API: drizzle-zod.

Детали: validate insert payload.

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

Туториал 7 — D1 SQLite

Команда или API: drizzle-orm/d1.

Детали: Cloudflare Workers.

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

Туториал 8 — Turso libsql

Команда или API: @libsql/client.

Детали: edge database.

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

Туториал 9 — PlanetScale

Команда или API: mysql dialect.

Детали: serverless MySQL.

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

Туториал 10 — SQL template

Команда или API: sql...``.

Детали: complex joins raw escape hatch.

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

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

  1. Column done integer toggle endpoint.

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

  1. leftJoin categories table.

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

  1. db.transaction insert two notes.

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

  1. Compare same CRUD on Prisma doc.

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

  1. drizzle-kit studio alternative TablePlus export.

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

  1. Partial index migration generate.

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

  1. NestJS DRIZZLE provider symbol pattern.

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

  1. Seed script with onConflictDoNothing.

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

  1. PostgreSQL switch from SQLite schema.

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

  1. Integration test with test.db file cleanup.

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


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

Drizzle Kit push?

drizzle-kit push dev only quick sync.

Type inference?

typeof notes.$inferSelect for row type.

Multiple schemas PG?

pgSchema('auth') for multi-tenant.

Better-sqlite3 sync?

Sync driver — только dev scripts.

Migrations squash?

Squash manually in git history carefully.

RLS PostgreSQL?

Policies outside Drizzle, raw SQL migration.

Batch insert?

insert values array chunk 500 rows.

Upsert?

onConflictDoUpdate target notes.id.

Logging queries?

logger: true in drizzle config dev.

Drizzle + Nest lifecycle?

OnModuleDestroy close sqlite.


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

#ПрактикаЗачем
1CommitCommit drizzle/ SQL migrations
2NeverNever commit dev.db
3postgres.jspostgres.js max connections tuned
4CICI migrate before deploy
5SeparateSeparate read/write URLs if replicas
6MonitorMonitor migration lock timeouts
7UseUse env DATABASE_URL only
8TestTest migrations on staging clone

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

День 2

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

День 3

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

День 4

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

День 5

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

День 6

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

День 7

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

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

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

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

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

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

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

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

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

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

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

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

Содержание