Database seeders
Database seeding is a way to set up your application with some initial data required to run and use the application. For example:
- Creating a seeder to insert countries, states, and cities before deploying and running your application.
- Or a seeder to insert users inside the database for local development.
The seeders are stored inside the database/seeders
directory. You can create a new seeder file by running the following Ace command.
node ace make:seeder User
# CREATE: database/seeders/user_seeder.ts
Every seeder file must extend the BaseSeeder
class and implement the run
method.
The following example uses a Lucid model to create multiple users. However, you can also use the Database query builder directly. In other words, seeders don't care what you write inside the run
method.
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import User from '#models/user'
export default class UserSeeder extends BaseSeeder {
async run() {
await User.createMany([
{
email: 'virk@adonisjs.com',
password: 'secret',
},
{
email: 'romain@adonisjs.com',
password: 'supersecret',
},
])
}
}
Running seeders
You can execute all or selected database seeders by running the following Ace command.
# runs all
node ace db:seed
You can define the --files
flag multiple times to run more than one file. Also, you will have to define the complete path to the seeder file. We opted for the complete path because your terminal shell can autocomplete the path for you.
node ace db:seed --files "./database/seeders/user_seeder.ts"
You can also select the seeder files interactively by running the db:seed
command in interactive mode.
node ace db:seed -i
Environment specific seeders
Lucid allows you to mark a seeder file to run only in a specific environment by changing the environment
property. This ensures you don't seed your production, testing, or development database with data you don't want by mistake.
The seeders using the environment
flag will only run when the NODE_ENV
environment variable is set to their respective value.
import { BaseSeeder } from '@adonisjs/lucid/seeders'
export default class UserSeeder extends BaseSeeder {
static environment = ['development', 'testing']
async run() {}
}
Idempotent operations
Unlike migrations, there is no tracking system in place for the database seeders. In other words, executing a seeder multiple times will perform the inserts multiple times as well.
Based upon the nature of a seeder, you may or may not want this behavior. For example:
- It is okay to run a
PostSeeder
multiple times and increase the number of posts you have in the database. - On the other hand, you would want the
CountrySeeder
to perform inserts only once. These kinds of seeders are idempotent.
Fortunately, Lucid models have inbuilt support for idempotent operations using updateOrCreate
or fetchOrCreateMany
. Continuing with the CountrySeeder
, the following is an example of creating countries only once.
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import Country from '#models/country'
export default class CountrySeeder extends BaseSeeder {
async run() {
const uniqueKey = 'isoCode'
await Country.updateOrCreateMany(uniqueKey, [
{
isoCode: 'IN',
name: 'India',
},
{
isoCode: 'FR',
name: 'France',
},
{
isoCode: 'TH',
name: ' Thailand',
},
])
}
}
In the above example, the updateOrCreateMany
method will look for existing rows inside the database using the isoCode
code and only inserts the missing ones and hence running the CountrySeeder
for multiple times will not insert duplicate rows.
Customizing database connection
The db:seed
command accepts an optional --connection
flag and forwards it to the seeder files as a connection
property. From there on, you can use this property to set the appropriate connection during your model interactions. For example:
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import User from '#models/user'
export default class UserSeeder extends BaseSeeder {
async run() {
await User.create(
{
email: 'virk@adonisjs.com',
password: 'secret',
},
{
connection: this.connection, // 👈
}
)
}
}
Now you can specify the --connection
flag on your db:seed
command, and the UserSeeder
will use it.
node ace db:seed --connection=tenant-1
Seeders config
The configuration for seeders is stored inside the config/database.ts
file under the connection config object.
paths
Define the paths for loading the database seeder files. You can also define a path to an installed package.
{
mysql: {
client: 'mysql2',
seeders: {
paths: ['./database/seeders', '@somepackage/seeders-dir']
}
}
}
Customizing seeders order
The db:seed
command runs all the seeders in the order they are stored on the filesystem.
If you want certain seeders to run before the other seeders, then either you can prefix a counter to file names or you can create a Main seeder directory as follows.
Step 1. Create the main seeder
Create the main seeder file by running the following Ace command.
node ace make:seeder main/index
# CREATE: database/seeders/main/index_seeder.ts
Step 2. Register its path inside the seeders
config
Open the config/database.ts
file and register the path to the main directory inside the connection config.
After the following change, the db:seed
command will scan the ./database/seeders/main
directory.
{
mysql: {
client: 'mysql2',
// ... rest of the config
seeders: {
paths: ['./database/seeders/main']
}
}
}
Step 3. Import other seeders inside the main seeder
Now, you can manually import all the seeders inside the index_seeder file and execute them in any order you want.
Following is an example implementation of the Main seeder. Feel free to customize it as per your requirements.
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import app from '@adonisjs/core/services/app'
export default class IndexSeeder extends BaseSeeder {
private async seed(Seeder: { default: typeof BaseSeeder }) {
/**
* Do not run when not in a environment specified in Seeder
*/
if (
!Seeder.default.environment ||
(!Seeder.default.environment.includes('development') && app.inDev) ||
(!Seeder.default.environment.includes('testing') && app.inTest) ||
(!Seeder.default.environment.includes('production') && app.inProduction)
) {
return
}
await new Seeder.default(this.client).run()
}
async run() {
await this.seed(await import('#database/seeders/category'))
await this.seed(await import('#database/seeders/user'))
await this.seed(await import('#database/seeders/post'))
}
}
Step 4. Run the db:seed
command
node ace db:seed
# completed database/seeders/main/index_seeder