Seeds & Factories

Once you’ve prepared your database schema with migrations, the next step is to add some data. This is where database seeds and factories come into the picture.

Seeds

Seeds are JavaScript classes containing a run method. Within the run method you are free to write any database related operations your seed requires.

Like migrations, a seed file is created using the adonis make command:

> adonis make:seed User
Output
✔ create  database/seeds/UserSeeder.js

Now open this file and type the following code inside it:

database/seeds/UserSeeder.js
const Factory = use('Factory')
const Database = use('Database')

class UserSeeder {
  async run () {
    const users = await Database.table('users')
    console.log(users)
  }
}

module.exports = UserSeeder

Run the seed file by calling the adonis seed command, which will execute the run method on all existing seed files.

Since you can write any database related code inside your seed files and execute them from the command line, they are helpful in offloading some tasks from your actual application code.

However, the real power of seeds is unlocked when combined with Factories.

Factories

Factories define data structures (blueprints) used to generate dummy data.

Factory blueprints are set inside the database/factory.js file:

const Factory = use('Factory')
const Hash = use('Hash')

Factory.blueprint('App/Models/User', async (faker) => {
  return {
    username: faker.username(),
    email: faker.email(),
    password: await Hash.make(faker.password())
  }
})

When a model instance is generated from a factory blueprint, the model’s attributes are prefilled using the keys defined inside the blueprint:

const user = await Factory
  .model('App/Models/User')
  .create()

Many model instances can be generated at the same time:

const usersArray = await Factory
  .model('App/Models/User')
  .createMany(5)

Creating Relationships

Say we want to create a User model and relate a Post to it.

For the example below, a posts relationship must first be defined on the User model. Learn more about relationships here.

First, create blueprints for both models in the database/factory.js file:

database/factory.js
// User blueprint
Factory.blueprint('App/Models/User', (faker) => {
  return {
    username: faker.username(),
    password: faker.password()
  }
})

// Post blueprint
Factory.blueprint('App/Models/Post', (faker) => {
  return {
    title: faker.sentence(),
    body: faker.paragraph()
  }
})

Then, create a User, make a Post, and associate both models to each other:

const user = await Factory.model('App/Models/User').create()
const post = await Factory.model('App/Models/Post').make()

await user.posts().save(post)

You may have noticed that we used the make method on the Post blueprint.

Unlike the create method, the make method does not persist the Post model to the database, instead returning an unsaved instance of the Post model pre-filled with dummy data (the Post model is saved when the .posts().save() method is called).

Seed Commands

Below is the list of available seed commands.

Command Options Description

adonis make:seed

None

Make a new seed file.

adonis seed

--files

Execute seed files (you can optionally pass a comma-separated list of --files to be executed, otherwise, all files get executed).

Model Factory API

Below is the list of available methods when using Lucid model factories.

create

Persist and return model instance:

await Factory
  .model('App/Models/User')
  .create()

createMany

Persist and return many model instances:

await Factory
  .model('App/Models/User')
  .createMany(3)

make

Return model instance but do not persist it to the database:

await Factory
  .model('App/Models/User')
  .make()

makeMany

Return array of model instances but do not persist them to the database:

await Factory
  .model('App/Models/User')
  .makeMany(3)

Usage Without Lucid

If your application doesn’t use Lucid models you can still use the Database Provider to generate factory database records.

blueprint

To define your factory blueprint without Lucid, pass a table name as the first parameter instead of a model name (e.g. users instead of App/Models/User):

Factory.blueprint('users', (faker) => {
  return {
    username: faker.username(),
    password: faker.password()
  }
})

create

Created a table record:

run () {
  await Factory.get('users').create()
}

table

Define a different table name at runtime:

await Factory
  .get('users')
  .table('my_users')
  .create()

returning

For PostgreSQL, define a returning column:

await Factory
  .get('users')
  .returning('id')
  .create()

connection

Choose a different connection at runtime:

await Factory
  .get('users')
  .connection('mysql')
  .returning('id')
  .create()

createMany

Create multiple records:

await Factory
  .get('users')
  .createMany(3)

Custom Data

The methods make, makeMany, create and createMany accept a custom data object which is passed directly to your blueprints.

For example:

const user = await Factory
  .model('App/Models/User')
  .create({ status: 'admin' })

Inside your blueprint, your custom data object is consumed like so:

Factory.blueprint('App/Models/User', async (faker, i, data) => {
  return {
    username: faker.username(),
    status: data.status
  }
})

Faker API

The faker object passed to a factory blueprint is a reference to the Chance random generator JavaScript library.

Make sure to read Chance’s documentation for the full list of available faker methods and properties.

FAQ’s

Since factories and seeds fit many different use cases you might be confused how/when to use them, so here is a list of frequently asked questions.

  1. Do factories and seeds have to be used together?
    No. Factories and seeds are not dependent upon each other and can be used independently. For example, you could just use seed files to import data into an AdonisJs app from a completely different app.

  2. Can I use factories when writing tests?
    Yes. Import the factory provider (Factory) into your test and use as required.

  3. Can I run only selected seed files?
    Yes. Passing --files with a list of comma-separated filenames to the adonis seed command ensures only those files are run, for example:

    > adonis seed --files='UsersSeeder.js, PostsSeeder.js'