Fullstack CourseLearn by building
Back to week 2

Topic

Seeders

Definition

A seed script is a program, separate from the migration history, that inserts baseline or demo data into an existing schema — typically checking whether that data already exists so the script is safe to run more than once.

In simpler words

A seed is a “make sure these demo accounts and sample rows exist” script — it never changes the schema, and running it twice should not create duplicates.

This course’s seed script is exactly how admin@course.local and member@course.local (password123) get into a fresh database.

After this you can

  • Explain why seed.ts checks for an existing row before creating one
  • Run migrations then seeds in the correct order on a fresh database
  • State the boundary between what belongs in a migration versus a seed

What seed.ts actually does

Definition

This repo’s seed script initializes the same DataSource used by the migration CLI, looks up each seed user by email, creates it only if missing, hashes its password with bcrypt, and inserts three sample tickets only if the tickets table is empty.

In simpler words

Every insert is guarded by a check — that is what makes it safe to run again without duplicating data.

users.findOne({ where: { email } }) before users.save(...) is the idempotency check for each seed user.

tickets.count() === 0 gates the sample tickets — once any ticket exists, the seed skips inserting more.

Idempotent user seed

let admin = await users.findOne({ where: { email: 'admin@course.local' } });
if (!admin) {
  admin = await users.save(
    users.create({
      email: 'admin@course.local',
      name: 'Course Admin',
      role: 'admin',
      passwordHash: await bcrypt.hash('password123', 12),
    }),
  );
}

bcrypt.hash(..., 12) never stores the plaintext password — even in seed data, hash it for real.

Seeds vs migrations

Definition

Migrations change what a database’s schema is (and occasionally essential reference data every environment must have), while seeds populate non-essential sample or development data on top of an already-migrated schema.

In simpler words

A migration should never be the place a demo user quietly gets created; a seed should never be the place a column quietly gets added.

If removing the seed script would break the app’s schema, that content belongs in a migration instead.

If removing a migration step would just mean fewer demo rows, that content belongs in a seed instead.

Boundary in one line

Migrations → schema shape + required reference data
Seeds      → demo/dev convenience data (admin/member users, sample tickets)

Keep schema changes reviewable in migrations; keep demo data disposable in seeds.

Running seeds locally

Definition

A seed script runs after the schema exists — after migrations have applied — since it inserts rows into tables the migrations are responsible for creating.

In simpler words

The correct fresh-database order is always: run migrations first, then run the seed.

pnpm db:seed (root alias for pnpm --filter @repo/api seed) runs apps/api/src/database/seed.ts via ts-node against the same DataSource the CLI uses.

After seeding, admin@course.local and member@course.local both work with password123 for logging in locally.

Fresh database bootstrap order

pnpm db:migration:run
pnpm db:seed
# admin@course.local / password123
# member@course.local / password123

Running db:seed before db:migration:run fails — the tables it inserts into do not exist yet.

Keep in mind

  • Guard every seed insert with an existence check — “run it twice safely” is the whole point.
  • Never hide a schema change inside a seed script.
  • Seed after migrating, every time, on a fresh database.

Test

Check your understanding

At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥ 80% (enforced by the API) to save progress.

Checking your session…

10 questions · concept 3 · syntax 2 · practical 3 · logic 2

Concept1. Which statement best defines Seeders?
Syntax2. Which code-level choice matches Seeders?
Practical3. A reviewer spots a bug related to Seeders. What is the right fix?
Logic4. Which reasoning correctly explains Seeders?
Concept5. Which boundary does correct use of Seeders preserve?
Practical6. What is the safest next step when applying Seeders?
Syntax7. Which implementation direction matches the rule for Seeders?
Logic8. Which consequence follows from applying Seeders correctly?
Concept9. Which claim about Seeders is true in this Nest + Postgres monorepo?
Practical10. Which team practice best demonstrates Seeders?