Database Seeders
Database seeders in Adonis ODM provide a convenient way to populate your MongoDB collections with test data, sample records, or initial application data. Seeders are particularly useful for development, testing, and setting up demo environments.
CLI Commands
Creating Seeders
make:odm-seeder
Generate a new seeder file with the proper structure:
# Create a basic seeder
node ace make:odm-seeder User
# Create seeder in subdirectory
node ace make:odm-seeder admin/User
Generated file structure:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
export default class UserSeeder extends BaseSeeder {
async run() {
// Add your seeding logic here
await User.createMany([
{
name: "John Doe",
email: "[email protected]",
},
{
name: "Jane Smith",
email: "[email protected]",
},
]);
}
}
Running Seeders
odm:seed
Execute seeders to populate your database:
# Run all seeders
node ace odm:seed
# Run specific seeder files
node ace odm:seed --files="./database/seeders/user_seeder.ts"
# Run multiple specific seeders
node ace odm:seed --files="./database/seeders/user_seeder.ts,./database/seeders/post_seeder.ts"
# Run seeders interactively (choose which ones to run)
node ace odm:seed --interactive
# Run seeders for specific connection
node ace odm:seed --connection=analytics
Options:
--files
- Comma-separated list of seeder files to run--interactive
- Choose seeders to run interactively--connection
- Specify database connection to use
Database Operations
Upcoming Commands
The following database management commands are planned for future releases:
# Test database connection (coming soon)
node ace mongodb:status
# Show database information (coming soon)
node ace mongodb:info
mongodb:status
(Coming Soon)
Check the status and connectivity of your MongoDB connections across all configured databases.
mongodb:info
(Coming Soon)
Display comprehensive information about your MongoDB setup including:
- Server version and configuration
- Database statistics and collection information
- Index information and performance metrics
- Connection pool status
Quick Start
1. Create Your First Seeder
node ace make:odm-seeder User
2. Define Seeding Logic
Edit the generated seeder file:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import { DateTime } from "luxon";
export default class UserSeeder extends BaseSeeder {
async run() {
// Create sample users
await User.createMany([
{
name: "Admin User",
email: "[email protected]",
role: "admin",
isActive: true,
createdAt: DateTime.now(),
},
{
name: "Regular User",
email: "[email protected]",
role: "user",
isActive: true,
createdAt: DateTime.now(),
},
{
name: "Test User",
email: "[email protected]",
role: "user",
isActive: false,
createdAt: DateTime.now().minus({ days: 30 }),
},
]);
console.log("✅ Users seeded successfully");
}
}
3. Run the Seeder
node ace odm:seed --files="./database/seeders/user_seeder.ts"
Advanced Features
Environment-Specific Seeders
Create different seeders for different environments:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import env from "#start/env";
export default class UserSeeder extends BaseSeeder {
async run() {
if (env.get("NODE_ENV") === "development") {
// Development data
await this.seedDevelopmentUsers();
} else if (env.get("NODE_ENV") === "testing") {
// Test data
await this.seedTestUsers();
} else {
// Production initial data
await this.seedProductionUsers();
}
}
private async seedDevelopmentUsers() {
await User.createMany([
{ name: "Dev User 1", email: "[email protected]" },
{ name: "Dev User 2", email: "[email protected]" },
// ... more development users
]);
}
private async seedTestUsers() {
await User.createMany([{ name: "Test User", email: "[email protected]" }]);
}
private async seedProductionUsers() {
// Only create essential admin user in production
await User.firstOrCreate(
{ email: "[email protected]" },
{
name: "System Admin",
email: "[email protected]",
role: "admin",
}
);
}
}
Execution Order and Dependencies
Control seeder execution order using dependencies:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import Post from "#models/post";
export default class PostSeeder extends BaseSeeder {
// Define dependencies - this seeder runs after UserSeeder
static dependencies = ["UserSeeder"];
async run() {
// Get existing users to create posts for
const users = await User.all();
if (users.length === 0) {
console.log("⚠️ No users found. Run UserSeeder first.");
return;
}
const posts = [];
for (const user of users) {
posts.push({
title: `Sample Post by ${user.name}`,
content: "This is a sample post content.",
authorId: user._id,
isPublished: true,
});
}
await Post.createMany(posts);
console.log(`✅ Created ${posts.length} posts`);
}
}
Main Seeders
Create a main seeder that orchestrates other seeders:
import { BaseSeeder } from "adonis-odm";
export default class MainSeeder extends BaseSeeder {
async run() {
// Run seeders in specific order
await this.call([
"UserSeeder",
"CategorySeeder",
"PostSeeder",
"CommentSeeder",
]);
}
}
Working with Different Data Types
Embedded Documents
Seed models with embedded documents:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
export default class UserWithProfileSeeder extends BaseSeeder {
async run() {
await User.createMany([
{
email: "[email protected]",
profile: {
firstName: "John",
lastName: "Doe",
bio: "Software developer with 5 years of experience.",
socialLinks: {
twitter: "@johndoe",
linkedin: "linkedin.com/in/johndoe",
},
},
addresses: [
{
type: "home",
street: "123 Main St",
city: "New York",
zipCode: "10001",
},
{
type: "work",
street: "456 Business Ave",
city: "New York",
zipCode: "10002",
},
],
},
]);
}
}
Referenced Relationships
Seed models with proper references:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import Post from "#models/post";
import Comment from "#models/comment";
export default class BlogSeeder extends BaseSeeder {
async run() {
// Create users first
const users = await User.createMany([
{ name: "Alice", email: "[email protected]" },
{ name: "Bob", email: "[email protected]" },
]);
// Create posts with user references
const posts = await Post.createMany([
{
title: "First Post",
content: "This is the first post.",
authorId: users[0]._id,
},
{
title: "Second Post",
content: "This is the second post.",
authorId: users[1]._id,
},
]);
// Create comments with post and user references
await Comment.createMany([
{
content: "Great post!",
postId: posts[0]._id,
authorId: users[1]._id,
},
{
content: "Thanks for sharing!",
postId: posts[1]._id,
authorId: users[0]._id,
},
]);
}
}
Connection-Specific Seeding
Multiple Database Connections
Seed different databases using specific connections:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import AnalyticsEvent from "#models/analytics_event";
export default class MultiConnectionSeeder extends BaseSeeder {
async run() {
// Seed main database
await User.createMany([
{ name: "User 1", email: "[email protected]" },
{ name: "User 2", email: "[email protected]" },
]);
// Seed analytics database
await AnalyticsEvent.createMany([
{
eventType: "page_view",
userId: "user1_id",
metadata: { page: "/home" },
},
{
eventType: "button_click",
userId: "user2_id",
metadata: { button: "signup" },
},
]);
}
}
Connection-Specific Seeder
# Run seeders only for analytics connection
node ace odm:seed --connection=analytics
import { BaseSeeder } from "adonis-odm";
import AnalyticsEvent from "#models/analytics_event";
export default class AnalyticsSeeder extends BaseSeeder {
// This seeder only runs for analytics connection
static connection = "analytics";
async run() {
await AnalyticsEvent.createMany([
{
eventType: "user_signup",
timestamp: new Date(),
metadata: { source: "web" },
},
{
eventType: "user_login",
timestamp: new Date(),
metadata: { source: "mobile" },
},
]);
}
}
Error Handling and Validation
Graceful Error Handling
Handle errors gracefully in your seeders:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import Logger from "@ioc:Adonis/Core/Logger";
export default class UserSeeder extends BaseSeeder {
async run() {
try {
// Check if users already exist
const existingUsers = await User.query().count();
if (existingUsers > 0) {
console.log("⚠️ Users already exist, skipping seeder");
return;
}
await User.createMany([
{ name: "John Doe", email: "[email protected]" },
{ name: "Jane Smith", email: "[email protected]" },
]);
console.log("✅ Users seeded successfully");
} catch (error) {
Logger.error("Failed to seed users:", error.message);
throw error; // Re-throw to stop seeding process
}
}
}
Data Validation
Validate data before seeding:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import { schema, rules } from "@ioc:Adonis/Core/Validator";
export default class UserSeeder extends BaseSeeder {
async run() {
const userData = [
{ name: "John Doe", email: "[email protected]", age: 30 },
{ name: "Jane Smith", email: "[email protected]", age: 25 },
];
// Validate each user data
const userSchema = schema.create({
name: schema.string(),
email: schema.string({}, [rules.email()]),
age: schema.number([rules.range(18, 100)]),
});
for (const data of userData) {
try {
await request.validate({ schema: userSchema, data });
await User.create(data);
} catch (validationError) {
console.log(`❌ Invalid data for user: ${data.name}`);
console.log(validationError.messages);
}
}
}
}
Conditional Seeding
Only seed when certain conditions are met:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import env from "#start/env";
export default class UserSeeder extends BaseSeeder {
async run() {
// Only run in development or testing
if (!["development", "testing"].includes(env.get("NODE_ENV"))) {
console.log("⚠️ Skipping UserSeeder in production environment");
return;
}
// Check if admin user already exists
const adminExists = await User.query().where("role", "admin").first();
if (adminExists) {
console.log("⚠️ Admin user already exists, skipping admin creation");
} else {
await User.create({
name: "Admin User",
email: "[email protected]",
role: "admin",
});
console.log("✅ Admin user created");
}
// Seed regular users
await this.seedRegularUsers();
}
private async seedRegularUsers() {
const userCount = await User.query().where("role", "user").count();
if (userCount < 10) {
const usersToCreate = 10 - userCount;
const users = Array.from({ length: usersToCreate }, (_, i) => ({
name: `User ${userCount + i + 1}`,
email: `user${userCount + i + 1}@example.com`,
role: "user",
}));
await User.createMany(users);
console.log(`✅ Created ${usersToCreate} regular users`);
}
}
}
Best Practices
1. Use Transactions for Data Integrity
Wrap seeding operations in transactions:
import { BaseSeeder } from "adonis-odm";
import db from "adonis-odm/services/db";
import User from "#models/user";
import Post from "#models/post";
export default class BlogSeeder extends BaseSeeder {
async run() {
await db.transaction(async (trx) => {
// Create users
const users = await User.createMany(
[
{ name: "Alice", email: "[email protected]" },
{ name: "Bob", email: "[email protected]" },
],
{ client: trx }
);
// Create posts
await Post.createMany(
[
{
title: "First Post",
content: "Content here",
authorId: users[0]._id,
},
{
title: "Second Post",
content: "More content",
authorId: users[1]._id,
},
],
{ client: trx }
);
console.log("✅ Blog data seeded successfully");
});
}
}
2. Make Seeders Idempotent
Ensure seeders can be run multiple times safely:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
export default class UserSeeder extends BaseSeeder {
async run() {
// Use updateOrCreate to avoid duplicates
await User.updateOrCreate(
{ email: "[email protected]" },
{
name: "Admin User",
email: "[email protected]",
role: "admin",
}
);
// Or check existence before creating
const users = [
{ name: "John Doe", email: "[email protected]" },
{ name: "Jane Smith", email: "[email protected]" },
];
for (const userData of users) {
const exists = await User.query().where("email", userData.email).first();
if (!exists) {
await User.create(userData);
}
}
}
}
3. Use Factories for Complex Data
Create factories for generating test data:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import { DateTime } from "luxon";
export default class UserSeeder extends BaseSeeder {
async run() {
// Create users using factory pattern
const users = await this.createUsers(50);
console.log(`✅ Created ${users.length} users`);
}
private async createUsers(count: number) {
const users = [];
for (let i = 1; i <= count; i++) {
users.push({
name: this.generateName(),
email: `user${i}@example.com`,
age: this.randomAge(),
isActive: Math.random() > 0.2, // 80% active
createdAt: this.randomDate(),
});
}
return await User.createMany(users);
}
private generateName(): string {
const firstNames = ["John", "Jane", "Alice", "Bob", "Charlie", "Diana"];
const lastNames = ["Doe", "Smith", "Johnson", "Brown", "Davis", "Wilson"];
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
return `${firstName} ${lastName}`;
}
private randomAge(): number {
return Math.floor(Math.random() * 50) + 18; // 18-67
}
private randomDate(): DateTime {
const daysAgo = Math.floor(Math.random() * 365);
return DateTime.now().minus({ days: daysAgo });
}
}
4. Organize Seeders by Feature
Structure your seeders logically:
database/
seeders/
main_seeder.ts
users/
user_seeder.ts
admin_seeder.ts
blog/
category_seeder.ts
post_seeder.ts
comment_seeder.ts
analytics/
event_seeder.ts
5. Use Environment Variables
Make seeders configurable:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
import env from "#start/env";
export default class UserSeeder extends BaseSeeder {
async run() {
const userCount = env.get("SEED_USER_COUNT", 10);
const adminEmail = env.get("ADMIN_EMAIL", "[email protected]");
// Create admin
await User.updateOrCreate(
{ email: adminEmail },
{
name: "Admin User",
email: adminEmail,
role: "admin",
}
);
// Create regular users
const users = Array.from({ length: userCount }, (_, i) => ({
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`,
role: "user",
}));
await User.createMany(users);
console.log(`✅ Created ${userCount} users and 1 admin`);
}
}
Troubleshooting
Common Issues
Seeder Not Found
If you get “Seeder not found” errors:
- Check file location: Ensure seeders are in
database/seeders/
- Verify file naming: Use snake_case for file names (e.g.,
user_seeder.ts
) - Check class export: Ensure the seeder class is the default export
// ✅ Correct
export default class UserSeeder extends BaseSeeder {
// ...
}
// ❌ Incorrect
export class UserSeeder extends BaseSeeder {
// ...
}
Connection Errors
If seeders fail to connect to MongoDB:
- Check environment variables: Verify
MONGO_CONNECTION_STRING
is set - Test connection: Use
mongodb:status
command (when available) - Check MongoDB service: Ensure MongoDB is running
Duplicate Key Errors
If you encounter duplicate key errors:
- Make seeders idempotent: Use
updateOrCreate
or check existence - Clear data first: Drop collections before seeding in development
- Use unique constraints: Ensure proper unique indexes
// Handle duplicates gracefully
try {
await User.create(userData);
} catch (error) {
if (error.code === 11000) {
// MongoDB duplicate key error
console.log(`User with email ${userData.email} already exists`);
} else {
throw error;
}
}
Performance Issues
Large Dataset Seeding
For seeding large amounts of data:
import { BaseSeeder } from "adonis-odm";
import User from "#models/user";
export default class LargeUserSeeder extends BaseSeeder {
async run() {
const batchSize = 1000;
const totalUsers = 50000;
for (let i = 0; i < totalUsers; i += batchSize) {
const batch = Array.from(
{ length: Math.min(batchSize, totalUsers - i) },
(_, j) => ({
name: `User ${i + j + 1}`,
email: `user${i + j + 1}@example.com`,
})
);
await User.createMany(batch);
console.log(
`✅ Created batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(totalUsers / batchSize)}`
);
}
}
}
Command Examples
Development Workflow
# Create and run a complete blog seeder setup
node ace make:odm-seeder User
node ace make:odm-seeder Category
node ace make:odm-seeder Post
node ace make:odm-seeder Comment
# Run all seeders
node ace odm:seed
# Run specific seeders in order
node ace odm:seed --files="./database/seeders/user_seeder.ts,./database/seeders/category_seeder.ts,./database/seeders/post_seeder.ts"
Testing Environment
# Run seeders for testing database
node ace odm:seed --connection=testing
# Run specific test data seeders
node ace odm:seed --files="./database/seeders/test_user_seeder.ts" --connection=testing
Interactive Seeding
# Choose which seeders to run
node ace odm:seed --interactive
This will present a menu like:
? Select seeders to run:
❯ ◯ UserSeeder
◯ CategorySeeder
◯ PostSeeder
◯ CommentSeeder
◯ All seeders
Next Steps
Now that you understand Database Seeders:
- Learn about Testing - Use seeders in your test suite
- Explore Transactions - Ensure data consistency
- Check Performance Tips - Optimize your seeding operations
- Review Error Handling - Handle seeding failures gracefully
Version Information
Database Seeders were introduced in Adonis ODM v0.2.0 with the following features:
- ✅ Basic seeder creation and execution
- ✅ Connection-specific seeding
- ✅ Interactive seeder selection
- ✅ Dependency management
- 🔄 Database status commands (coming soon)
- 🔄 Advanced seeder scheduling (planned)