Seeding

To end the core module, let's allow for the insertion of a bunch of fictional data to see the database populated.

Seeding is about loading the database with mocked entities and relations between them to test the system's functionalities.

TypeORM currently has no support for seeding, so we can write a route for doing such. For organization, let's create a very simple seeding resource inside the database structure. Choose no for CRUD entry points.

nest g res database/seeding

In the controller, insert the route responsible for seeding.

@Post()
seed() {
  return this.seedingService.seed();
}

In the service, we need to perform several database operations and all of them need to be successful. In case any one fails, all of the already performed operations need to be reverted back in order to avoid inconsistencies. This means that we need to use a transaction.

To do so, first we need to inject in the service a DataSource.

constructor(private readonly dataSource: DataSource) {}

Now, inside the seed() method, insert the basic transaction structure inside which we'll insert the actual seed. You can consider this as boilerplate.

async seed() {
  const queryRunner = this.dataSource.createQueryRunner();
  await queryRunner.connect();
  await queryRunner.startTransaction();
  try {
    // seed goes here

    await queryRunner.commitTransaction();
  } catch (error) {
    await queryRunner.rollbackTransaction();
    throw error;
  } finally {
    await queryRunner.release();
  }
}

Now we can use the seed itself. The following steps occur

  • First, repositories are obtained from the queryRunner. Only these repositories are able to rollback in case of a failed operation.

  • After that, the database tables are cleared. Note that some of them are not cleared directly because their records will be deleted due to cascade.

  • Finally, the entities and their relationships are created and saved in the database.

const usersRepository = queryRunner.manager.getRepository(User);
const categoriesRepository = queryRunner.manager.getRepository(Category);
const productsRepository = queryRunner.manager.getRepository(Product);
const ordersRepository = queryRunner.manager.getRepository(Order);
const orderItemsRepository = queryRunner.manager.getRepository(OrderItem);
const paymentsRepository = queryRunner.manager.getRepository(Payment);

const orders = await ordersRepository.find();
await ordersRepository.remove(orders);
const users = await usersRepository.find();
await usersRepository.remove(users);
const products = await productsRepository.find();
await productsRepository.remove(products);
const categories = await categoriesRepository.find();
await categoriesRepository.remove(categories);

const cat1 = categoriesRepository.create({ name: 'Electronics' });
const cat2 = categoriesRepository.create({ name: 'Books' });
const cat3 = categoriesRepository.create({ name: 'Computers' });
const cat4 = categoriesRepository.create({ name: 'Games' });

await categoriesRepository.save([cat1, cat2, cat3, cat4]);

const p1 = productsRepository.create({
  name: 'Book of Cain',
  description: 'The writings of an elderly scholar about this perilous world.',
  price: 102.5,
  categories: [cat2],
});
const p2 = productsRepository.create({
  name: 'Smart TV',
  price: 2350,
  categories: [cat1, cat3],
});
const p3 = productsRepository.create({
  name: 'Macbook Pro',
  price: 1200,
  categories: [cat3],
});
const p4 = productsRepository.create({
  name: 'Gaming PC',
  description: 'Latest generation hardware for the best experience.',
  price: 2000,
  categories: [cat3],
});
const p5 = productsRepository.create({
  name: 'Game Mechanics: Advanced Game Design',
  description: 'Learn how to craft well-designed game mechanics.',
  price: 149.9,
  categories: [cat2],
});
const p6 = productsRepository.create({
  name: 'Warcraft III: Reign of Chaos',
  description: 'A true classic in the RTS genre.',
  price: 25.99,
  categories: [cat4],
});

await productsRepository.save([p1, p2, p3, p4, p5, p6]);

const u1 = usersRepository.create({
  name: 'Pedro Faria',
  email: 'jarulf@mail.com',
  phone: '988888888',
  password: '123456',
});
const u2 = usersRepository.create({
  name: 'Chris Metzen',
  email: 'chris@blizz.com',
  phone: '977777777',
  password: '654321',
});

await usersRepository.save([u1, u2]);

const oi1 = orderItemsRepository.create({
  product: p1,
  quantity: 2,
  price: p1.price,
});
const oi2 = orderItemsRepository.create({
  product: p3,
  quantity: 1,
  price: p3.price,
});
const oi3 = orderItemsRepository.create({
  product: p3,
  quantity: 2,
  price: p3.price,
});
const oi4 = orderItemsRepository.create({
  product: p5,
  quantity: 2,
  price: p5.price,
});

const pay1 = paymentsRepository.create();

const o1 = ordersRepository.create({
  customer: u1,
  items: [oi1, oi2],
  status: OrderStatus.AWAITING_SHIPMENT,
  payment: pay1,
});
const o2 = ordersRepository.create({
  customer: u2,
  items: [oi3],
  status: OrderStatus.AWAITING_PAYMENT,
});
const o3 = ordersRepository.create({
  customer: u1,
  items: [oi4],
  status: OrderStatus.AWAITING_PAYMENT,
});

await ordersRepository.save([o1, o2, o3]);

In the Bonus Module 6 - Automated Testing (TBA), we'll learn how to perform these same operations by emulating requests from a user. This is possible with the supertest library. We'll also do so in a dedicated database purposed for testing.

Commit - Seeding

Last updated