← Назад к вопросам

Что такое Seeds в БД?

1.0 Junior🔥 241 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Seeds (Семена) в базах данных

Seeds — это исходные данные, которые загружаются в базу данных при инициализации или при настройке development окружения. Это не тестовые данные, а необходимый минимум для работы приложения.

Что такое Seeds?

Seeds (семена) — это скрипты, которые заполняют БД начальными данными:

  • Справочные данные (категории, статусы, роли)
  • Демо-данные для development
  • Конфигурационные параметры
  • Тестовые пользователи
  • Константы приложения

Аналогия: если миграция создает структуру, то seed наполняет ее смыслом.

Зачем нужны Seeds?

1. Development окружение

Всем разработчикам нужны одинаковые тестовые данные для работы:

# Вместо того, чтобы каждый вручную создавал:
# - категории товаров
# - тестовых пользователей
# - справочные данные

# Делаем один раз:
npm run seed

2. Миграция между окружениями

При развертывании на production нужны базовые данные (роли, статусы):

# На production после миграции
docker exec app npm run seed:prod

3. Тестирование

Тесты требуют известное состояние БД:

beforeEach(async () => {
  // Seed test database
  await seedTestData();
});

test('должен найти пользователя', async () => {
  // У нас уже есть тестовые данные
  const user = await userService.findById('test-user-id');
  expect(user).toBeDefined();
});

4. Демонстрация и прототипирование

Для клиентов и стейкхолдеров нужен реалистичный данные:

# Запускаем demo версию с красивыми данными
npm run seed:demo
npm run dev

Примеры Seeds

Пример 1: Справочные данные (Knex seeder)

// seeds/001_insert_roles.ts
export async function seed(knex: Knex): Promise<void> {
  // Удаляем существующие роли
  await knex('roles').del();
  
  // Вставляем базовые роли
  await knex('roles').insert([
    {
      id: 1,
      name: 'admin',
      description: 'Administrator role',
      created_at: new Date(),
    },
    {
      id: 2,
      name: 'user',
      description: 'Regular user role',
      created_at: new Date(),
    },
    {
      id: 3,
      name: 'moderator',
      description: 'Moderator role',
      created_at: new Date(),
    },
  ]);
}

Пример 2: Тестовые пользователи (TypeORM seeder)

// src/database/seeds/user.seeder.ts
import { DataSource } from 'typeorm';
import { User } from '../entities/User';
import * as bcrypt from 'bcrypt';

export async function seedUsers(dataSource: DataSource): Promise<void> {
  const userRepository = dataSource.getRepository(User);
  
  const users = [
    {
      email: 'admin@example.com',
      password: await bcrypt.hash('admin123', 10),
      role: 'admin',
      name: 'Admin User',
    },
    {
      email: 'user@example.com',
      password: await bcrypt.hash('user123', 10),
      role: 'user',
      name: 'Test User',
    },
  ];
  
  // Проверяем, что пользователи еще не существуют
  for (const userData of users) {
    const exists = await userRepository.findOne({
      where: { email: userData.email },
    });
    
    if (!exists) {
      await userRepository.save(userData);
    }
  }
}

Пример 3: Категории товаров

// seeds/categories.ts
export async function seed(dataSource: DataSource) {
  const categoryRepo = dataSource.getRepository(Category);
  
  const categories = [
    { name: 'Electronics', slug: 'electronics' },
    { name: 'Books', slug: 'books' },
    { name: 'Clothing', slug: 'clothing' },
    { name: 'Food', slug: 'food' },
  ];
  
  for (const cat of categories) {
    await categoryRepo.save({
      ...cat,
      createdAt: new Date(),
    });
  }
}

Пример 4: Комплексный seed с связями

// seeds/populate-store.ts
export async function seed(dataSource: DataSource) {
  const userRepo = dataSource.getRepository(User);
  const productRepo = dataSource.getRepository(Product);
  const orderRepo = dataSource.getRepository(Order);
  
  // 1. Создаем пользователя
  const user = await userRepo.save({
    email: 'customer@example.com',
    name: 'John Doe',
  });
  
  // 2. Создаем товары
  const products = await productRepo.save([
    { name: 'Laptop', price: 999.99 },
    { name: 'Phone', price: 599.99 },
    { name: 'Headphones', price: 199.99 },
  ]);
  
  // 3. Создаем заказы
  await orderRepo.save({
    user,
    products: [products[0]],
    total: 999.99,
    status: 'completed',
  });
}

Seeds vs Migrations

Миграции (Migrations):

-- up: создаёт таблицу
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email VARCHAR(255)
);

-- down: удаляет таблицу
DROP TABLE users;

Seeds:

// Вставляет данные после создания таблицы
await userRepository.insert({
  email: 'test@example.com'
});

Ключевое различие:

  • Миграция — это структура (CREATE, ALTER, DROP)
  • Seed — это содержимое (INSERT, UPDATE)

Лучшие практики Seeds

1. Идемпотентность (Idempotency)

Seed должен работать много раз без ошибок:

// Правильно: проверяем существование
const exists = await roleRepo.findOne({ where: { name: 'admin' } });
if (!exists) {
  await roleRepo.save({ name: 'admin' });
}

// Неправильно: просто вставляем (вызовет ошибку при повторном запуске)
await roleRepo.save({ name: 'admin' });

2. Разные seeds для разных окружений

// seeds/development.seed.ts — много demo данных
export async function seed() {
  await seedCategories();
  await seedManyProducts(1000);
  await seedTestUsers(100);
}

// seeds/production.seed.ts — только критичное
export async function seed() {
  await seedRoles();
  await seedCountries();
}

3. Семена должны быть воспроизводимы

// Правильно: определенные данные
const categories = [
  { id: 1, name: 'Electronics' },
  { id: 2, name: 'Books' },
];

// Неправильно: случайные данные
const name = Math.random().toString();

4. Документирование

/**
 * Seeds для development окружения
 * 
 * Создает:
 * - Admin user (email: admin@example.com, password: admin123)
 * - 3 обычных пользователя
 * - 5 категорий товаров
 * - 50 тестовых товаров
 */
export async function seed() {
  // ...
}

Seeds в Node.js фреймворках

Express/Fastify с Knex:

npm install knex
npx knex seed:make create_initial_data
npm run knex:seed

TypeORM:

npm run typeorm migration:run
npm run seed  # Custom seed runner

Prisma:

// prisma/seed.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
  // Seed data
}

main()
  .catch(e => process.exit(1))
  .finally(() => prisma.$disconnect());
// package.json
{
  "prisma": {
    "seed": "ts-node prisma/seed.ts"
  }
}
pprisma db seed

Workflow в команде

1. Разработчик создает миграцию

npm run migration:create add_categories_table

2. Разработчик создает seed

npm run seed:create populate_categories

3. Другие разработчики затягивают оба

git pull
npm run migration:run
npm run seed

4. На production (DevOps)

# After deployment
docker exec app npm run migration:run
docker exec app npm run seed:prod

Заключение

Seeds — это критичный инструмент для:

  • Консистентности окружений
  • Автоматизации setup
  • Тестирования
  • Демонстрации

Хороший seed спасает часы разработки и предотвращает ошибки, связанные с различиями в данных между разработчиками.