data-seeding
Create or update database seed scripts for development and testing environments. Use when setting up test data or initializing development databases.
When & Why to Use This Skill
This Claude skill automates the creation and management of database seed scripts, streamlining the process of initializing development, testing, and staging environments. It enables developers to efficiently generate realistic mock data, handle complex table relationships, and maintain consistent database states across different stages of the software development lifecycle (SDLC).
Use Cases
- Rapidly initializing local development databases with sample records to begin coding and UI testing immediately.
- Generating large-scale, realistic datasets using Faker.js for performance benchmarking and staging environment simulations.
- Creating deterministic and isolated data sets for automated testing suites to ensure consistent and reproducible CI/CD results.
- Importing bulk data from legacy systems or external sources via JSON and CSV files into structured database schemas.
- Implementing environment-specific seeding strategies, such as minimal data for unit tests versus comprehensive datasets for product demonstrations.
| name | data-seeding |
|---|---|
| description | Create or update database seed scripts for development and testing environments. Use when setting up test data or initializing development databases. |
| allowed-tools | Read, Edit, Write, Bash |
Data Seeding Skill
This skill helps you create and manage database seed scripts in packages/database/.
When to Use This Skill
- Setting up development database with test data
- Creating seed data for testing
- Initializing demo environments
- Resetting database to known state
- Generating realistic sample data
- Testing data migration scripts
Seed Script Structure
packages/database/
├── src/
│ └── seed/
│ ├── index.ts # Main seed runner
│ ├── cars.ts # Car data seeds
│ ├── coe.ts # COE data seeds
│ ├── posts.ts # Blog posts seeds
│ └── users.ts # User seeds
├── scripts/
│ └── seed.ts # Seed execution script
└── package.json
Basic Seed Pattern
Simple Seed Script
// packages/database/src/seed/cars.ts
import { db } from "../index";
import { cars } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedCars() {
console.log("Seeding cars...");
const carData = [
{
id: nanoid(),
make: "Toyota",
model: "Camry",
vehicleClass: "Sedan",
fuelType: "Petrol",
month: "2024-01",
number: 150,
},
{
id: nanoid(),
make: "Honda",
model: "Civic",
vehicleClass: "Sedan",
fuelType: "Petrol",
month: "2024-01",
number: 120,
},
{
id: nanoid(),
make: "BMW",
model: "3 Series",
vehicleClass: "Sedan",
fuelType: "Petrol",
month: "2024-01",
number: 80,
},
];
await db.insert(cars).values(carData);
console.log(`✓ Seeded ${carData.length} cars`);
}
Main Seed Runner
// packages/database/src/seed/index.ts
import { db } from "../index";
import { seedCars } from "./cars";
import { seedCOE } from "./coe";
import { seedPosts } from "./posts";
export async function seed() {
try {
console.log("🌱 Starting database seed...\n");
// Clear existing data (optional)
await clearDatabase();
// Run seed functions
await seedCars();
await seedCOE();
await seedPosts();
console.log("\n✅ Database seeded successfully!");
} catch (error) {
console.error("❌ Seed failed:", error);
throw error;
}
}
async function clearDatabase() {
console.log("Clearing existing data...");
// Delete in reverse order of dependencies
await db.delete(posts);
await db.delete(coe);
await db.delete(cars);
console.log("✓ Database cleared\n");
}
// Run if called directly
if (require.main === module) {
seed()
.then(() => process.exit(0))
.catch(() => process.exit(1));
}
Execution Script
// packages/database/scripts/seed.ts
import { seed } from "../src/seed";
seed()
.then(() => {
console.log("Seed completed");
process.exit(0);
})
.catch((error) => {
console.error("Seed failed:", error);
process.exit(1);
});
Add to package.json:
{
"scripts": {
"db:seed": "tsx scripts/seed.ts"
}
}
Run seed:
pnpm -F @sgcarstrends/database db:seed
Advanced Seed Patterns
Seed with Relationships
// packages/database/src/seed/posts.ts
import { db } from "../index";
import { users, posts } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedPosts() {
console.log("Seeding users and posts...");
// First, create users
const userData = [
{
id: nanoid(),
name: "John Doe",
email: "john@example.com",
},
{
id: nanoid(),
name: "Jane Smith",
email: "jane@example.com",
},
];
const createdUsers = await db.insert(users).values(userData).returning();
// Then, create posts with user references
const postData = [
{
id: nanoid(),
title: "First Blog Post",
content: "This is the first blog post content.",
authorId: createdUsers[0].id,
published: true,
publishedAt: new Date(),
},
{
id: nanoid(),
title: "Second Blog Post",
content: "This is the second blog post content.",
authorId: createdUsers[1].id,
published: true,
publishedAt: new Date(),
},
];
await db.insert(posts).values(postData);
console.log(`✓ Seeded ${createdUsers.length} users and ${postData.length} posts`);
}
Seed with Faker.js
pnpm -F @sgcarstrends/database add -D @faker-js/faker
// packages/database/src/seed/realistic-data.ts
import { faker } from "@faker-js/faker";
import { db } from "../index";
import { cars } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedRealisticCars(count: number = 100) {
console.log(`Seeding ${count} realistic cars...`);
const makes = ["Toyota", "Honda", "BMW", "Mercedes", "Audi", "Nissan", "Mazda"];
const fuelTypes = ["Petrol", "Diesel", "Electric", "Hybrid"];
const vehicleClasses = ["Sedan", "SUV", "Hatchback", "MPV"];
const carData = Array.from({ length: count }, () => ({
id: nanoid(),
make: faker.helpers.arrayElement(makes),
model: faker.vehicle.model(),
vehicleClass: faker.helpers.arrayElement(vehicleClasses),
fuelType: faker.helpers.arrayElement(fuelTypes),
month: faker.date.between({ from: "2020-01-01", to: "2024-12-31" })
.toISOString()
.slice(0, 7),
number: faker.number.int({ min: 10, max: 500 }),
}));
// Batch insert for performance
const batchSize = 50;
for (let i = 0; i < carData.length; i += batchSize) {
const batch = carData.slice(i, i + batchSize);
await db.insert(cars).values(batch);
}
console.log(`✓ Seeded ${count} realistic cars`);
}
Seed from JSON File
// packages/database/src/seed/from-file.ts
import { db } from "../index";
import { cars } from "../db/schema";
import { readFileSync } from "fs";
import { join } from "path";
export async function seedFromJSON() {
console.log("Seeding from JSON file...");
// Read data from JSON file
const filePath = join(__dirname, "../../data/cars.json");
const rawData = readFileSync(filePath, "utf-8");
const carData = JSON.parse(rawData);
await db.insert(cars).values(carData);
console.log(`✓ Seeded ${carData.length} cars from JSON`);
}
Example JSON file:
// packages/database/data/cars.json
[
{
"id": "car-1",
"make": "Toyota",
"model": "Camry",
"vehicleClass": "Sedan",
"fuelType": "Petrol",
"month": "2024-01",
"number": 150
},
{
"id": "car-2",
"make": "Honda",
"model": "Civic",
"vehicleClass": "Sedan",
"fuelType": "Petrol",
"month": "2024-01",
"number": 120
}
]
Seed from CSV
pnpm -F @sgcarstrends/database add -D csv-parse
// packages/database/src/seed/from-csv.ts
import { db } from "../index";
import { cars } from "../db/schema";
import { parse } from "csv-parse/sync";
import { readFileSync } from "fs";
import { join } from "path";
import { nanoid } from "nanoid";
export async function seedFromCSV() {
console.log("Seeding from CSV file...");
const filePath = join(__dirname, "../../data/cars.csv");
const csvContent = readFileSync(filePath, "utf-8");
const records = parse(csvContent, {
columns: true,
skip_empty_lines: true,
});
const carData = records.map((record: any) => ({
id: nanoid(),
make: record.make,
model: record.model,
vehicleClass: record.vehicle_class,
fuelType: record.fuel_type,
month: record.month,
number: parseInt(record.number, 10),
}));
// Batch insert
const batchSize = 100;
for (let i = 0; i < carData.length; i += batchSize) {
const batch = carData.slice(i, i + batchSize);
await db.insert(cars).values(batch);
}
console.log(`✓ Seeded ${carData.length} cars from CSV`);
}
Environment-Specific Seeds
Development Seeds
// packages/database/src/seed/dev.ts
import { seedCars } from "./cars";
import { seedCOE } from "./coe";
import { seedRealisticCars } from "./realistic-data";
export async function seedDevelopment() {
console.log("🔧 Seeding development environment...\n");
// Small, predictable dataset
await seedCars();
await seedCOE();
console.log("\n✅ Development seed complete!");
}
Testing Seeds
// packages/database/src/seed/test.ts
export async function seedTesting() {
console.log("🧪 Seeding test environment...\n");
// Minimal, deterministic data for tests
const testCar = {
id: "test-car-1",
make: "Toyota",
model: "Camry",
vehicleClass: "Sedan",
fuelType: "Petrol",
month: "2024-01",
number: 100,
};
await db.insert(cars).values([testCar]);
console.log("\n✅ Test seed complete!");
}
Staging Seeds
// packages/database/src/seed/staging.ts
export async function seedStaging() {
console.log("🎭 Seeding staging environment...\n");
// Larger, realistic dataset similar to production
await seedRealisticCars(1000);
await seedRealisticCOE(500);
await seedRealisticPosts(50);
console.log("\n✅ Staging seed complete!");
}
Conditional Seeding
// packages/database/src/seed/index.ts
import { seedDevelopment } from "./dev";
import { seedTesting } from "./test";
import { seedStaging } from "./staging";
export async function seed() {
const env = process.env.NODE_ENV || "development";
switch (env) {
case "development":
await seedDevelopment();
break;
case "test":
await seedTesting();
break;
case "staging":
await seedStaging();
break;
default:
throw new Error(`No seed strategy for environment: ${env}`);
}
}
Idempotent Seeds
Upsert Pattern
// packages/database/src/seed/idempotent.ts
import { db } from "../index";
import { cars } from "../db/schema";
export async function seedIdempotent() {
console.log("Seeding with upsert...");
const carData = [
{
id: "fixed-id-1",
make: "Toyota",
model: "Camry",
month: "2024-01",
number: 150,
},
];
// Use onConflictDoUpdate for upsert
await db
.insert(cars)
.values(carData)
.onConflictDoUpdate({
target: cars.id,
set: {
make: carData[0].make,
model: carData[0].model,
number: carData[0].number,
updatedAt: new Date(),
},
});
console.log("✓ Upserted cars");
}
Check Before Insert
export async function seedIfEmpty() {
console.log("Checking if database needs seeding...");
const existingCars = await db.select().from(cars).limit(1);
if (existingCars.length > 0) {
console.log("Database already has data, skipping seed");
return;
}
console.log("Database is empty, proceeding with seed");
await seedCars();
}
Performance Optimization
Batch Inserts
export async function seedLargeDataset() {
const totalRecords = 10000;
const batchSize = 1000;
console.log(`Seeding ${totalRecords} records in batches of ${batchSize}...`);
for (let i = 0; i < totalRecords; i += batchSize) {
const batch = generateBatch(batchSize);
await db.insert(cars).values(batch);
console.log(`Progress: ${Math.min(i + batchSize, totalRecords)}/${totalRecords}`);
}
console.log("✓ Large dataset seeded");
}
function generateBatch(size: number) {
return Array.from({ length: size }, () => ({
id: nanoid(),
make: "Toyota",
model: "Camry",
month: "2024-01",
number: 100,
}));
}
Use Transactions
import { db } from "../index";
export async function seedWithTransaction() {
console.log("Seeding with transaction...");
await db.transaction(async (tx) => {
// All or nothing
await tx.insert(cars).values([...carData]);
await tx.insert(coe).values([...coeData]);
await tx.insert(posts).values([...postData]);
});
console.log("✓ Transaction seed complete");
}
Utility Functions
Generate Realistic Dates
function generateMonthRange(start: string, end: string): string[] {
const months = [];
let current = new Date(start);
const endDate = new Date(end);
while (current <= endDate) {
months.push(current.toISOString().slice(0, 7));
current.setMonth(current.getMonth() + 1);
}
return months;
}
// Usage
const months = generateMonthRange("2020-01", "2024-12");
Generate Sequential IDs
function generateSequentialId(prefix: string, index: number): string {
return `${prefix}-${String(index).padStart(5, "0")}`;
}
// Usage
const id = generateSequentialId("car", 42); // "car-00042"
Testing Seeds
// packages/database/src/seed/__tests__/seed.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { db } from "../../index";
import { cars } from "../../db/schema";
import { seedCars } from "../cars";
describe("Seed Scripts", () => {
beforeEach(async () => {
// Clear data before each test
await db.delete(cars);
});
it("seeds cars successfully", async () => {
await seedCars();
const result = await db.select().from(cars);
expect(result.length).toBeGreaterThan(0);
expect(result[0].make).toBeDefined();
});
it("creates valid data", async () => {
await seedCars();
const [car] = await db.select().from(cars).limit(1);
expect(car.id).toBeTruthy();
expect(car.make).toBeTruthy();
expect(car.number).toBeGreaterThanOrEqual(0);
});
});
CLI for Selective Seeding
// packages/database/scripts/seed-cli.ts
import { seedCars } from "../src/seed/cars";
import { seedCOE } from "../src/seed/coe";
import { seedPosts } from "../src/seed/posts";
const seeders = {
cars: seedCars,
coe: seedCOE,
posts: seedPosts,
all: async () => {
await seedCars();
await seedCOE();
await seedPosts();
},
};
const target = process.argv[2] as keyof typeof seeders;
if (!target || !seeders[target]) {
console.error("Usage: pnpm db:seed [cars|coe|posts|all]");
process.exit(1);
}
seeders[target]()
.then(() => {
console.log(`✅ Seeded ${target} successfully`);
process.exit(0);
})
.catch((error) => {
console.error(`❌ Seed failed:`, error);
process.exit(1);
});
Add to package.json:
{
"scripts": {
"db:seed": "tsx scripts/seed-cli.ts all",
"db:seed:cars": "tsx scripts/seed-cli.ts cars",
"db:seed:coe": "tsx scripts/seed-cli.ts coe",
"db:seed:posts": "tsx scripts/seed-cli.ts posts"
}
}
Common Seed Data
COE Seed Data
// packages/database/src/seed/coe.ts
import { db } from "../index";
import { coe } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedCOE() {
console.log("Seeding COE data...");
const coeData = [
{
id: nanoid(),
biddingNo: 1,
month: "2024-01",
vehicleClass: "A",
quota: 1000,
bidsReceived: 1200,
premium: "95000.00",
},
{
id: nanoid(),
biddingNo: 1,
month: "2024-01",
vehicleClass: "B",
quota: 800,
bidsReceived: 900,
premium: "105000.00",
},
];
await db.insert(coe).values(coeData);
console.log(`✓ Seeded ${coeData.length} COE records`);
}
References
- Related files:
packages/database/src/seed/- Seed scriptspackages/database/scripts/seed.ts- Seed runnerpackages/database/CLAUDE.md- Database package documentation
Best Practices
- Idempotent: Seeds should be safe to run multiple times
- Environment-Specific: Different seed data for dev/test/staging
- Realistic Data: Use Faker for realistic test data
- Batch Inserts: Use batching for large datasets
- Relationships: Seed in correct order (parent tables first)
- Transactions: Use transactions for atomic seeding
- Clear First: Option to clear existing data
- Logging: Provide clear progress feedback