How to seed a database with TypeORM and Faker in 2023
So, I was playing around with TypeORM and at some point, I needed to populate my database with some data to be able to test some features as they could be in real-world situations. Thankfully, we got our back covered thanks to the open-source community with the package typeorm-extension
. It is a great package that just allows us to drop, create and seed databases using TypeORM. But we are most interested in the seeding part.
Enough Talk and Bring the Code!!
Naturally, we are going to install typeorm-extension
and faker
for genarting fake datas. And Of course, we need to install TypeORM and a database adapter, in our case we are going to use MySQL:
yarn add typeorm-extension @faker-js/faker typeorm reflect-metadata mysql
Then, let's assume that these are our entities:
import {
Entity,
Column,
PrimaryGeneratedColumn,
OneToMany,
} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id?: string;
@Column()
userName?: string;
@OneToMany(() => Post, (post) => post.author)
posts?: Post[];
}
import typeorm, {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
} from "typeorm";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id?: string;
@Column()
title!: string;
@Column()
content!: string;
@ManyToOne(() => User, (user) => user.posts)
author!: typeorm.Relation<User>;
}
Factory in typeorm-extension for seeding
It is one of the two main concepts brought by the library we are about to use: typeorm-extension
.
Basically for each entity we have in our application, we will define a factory and those will be responsible for generating the data that will populate our application. Each of these data corresponds to the properties that we have defined in our entity.
Starting with the User
entity, the userName
property should be generated so the corresponding factory would look like this:
import { Faker } from "@faker-js/faker";
import { setSeederFactory } from "typeorm-extension";
import { User } from "./users.entity";
export const UsersFactory = setSeederFactory(User, (faker: Faker) => {
const user = new User();
user.userName = faker.internet.userName();
return user;
});
And for the Post
entity, both title
and content
is being generated like so:
import { Faker } from "@faker-js/faker";
import { setSeederFactory } from "typeorm-extension";
import { Post } from "./posts.entity";
export const PostsFactory = setSeederFactory(Post, (faker: Faker) => {
const post = new Post();
post.title = faker.lorem.sentence();
post.content = faker.lorem.sentence();
return post;
});
Now with our factories defined, as soon as we define how many users or articles we want to create, the factory will always generate random values in each of the properties thanks to faker.js.
The Seeder in typeorm-extension
The second concept here is the Seeder.
A Seeder is a class we have to define to run the factories created above. In other words, we call the factories inside a Seeder then we call the Seeder to seed the database.
A seeder class must implement the Seeder
interface. You can have as many seeders as you want. But to make things simpler in general, I think only one is necessary and we'll put it in a file named main.seeder.ts
. The minimal code needed to create a seeder is as follows:
import { DataSource } from "typeorm";
import { Seeder, SeederFactoryManager } from "typeorm-extension";
export default class MainSeeder implements Seeder {
public async run(
dataSource: DataSource,
factoryManager: SeederFactoryManager,
): Promise<any> {
// Run the factories here
}
}
Now let's seed the User
entity:
import { DataSource } from "typeorm";
import { Seeder, SeederFactoryManager } from "typeorm-extension";
import { User } from "./users.entity";
export default class MainSeeder implements Seeder {
public async run(
dataSource: DataSource,
factoryManager: SeederFactoryManager,
): Promise<any> {
const userFactory = factoryManager.get(User);
const users = await userFactory.saveMany(7);
}
}
As simple as that, what we've done so far is generate and save 7 users to the database.
Now let's see how to seed the posts. This time, in addition to the number of posts to create, we also must provide the author of each post. Plus, we want to assign it randomly. Here is how to achieve it:
import { DataSource } from "typeorm";
import { Seeder, SeederFactoryManager } from "typeorm-extension";
import { faker } "@faker-js/faker";
import { User } from "./users.entity";
import { Post } from "./posts.entity";
export class MainSeeder implements Seeder {
public async run(
dataSource: DataSource,
factoryManager: SeederFactoryManager,
): Promise<any> {
const postsRepository = dataSource.getRepository(Post);
const userFactory = factoryManager.get(User);
const postsFactory = factoryManager.get(Post);
const users = await userFactory.saveMany(7);
const posts = await Promise.all(
Array(17)
.fill("")
.map(async () => {
const made = await postsFactory.make({
author: faker.helpers.arrayElement(users),
});
return made;
}),
);
await postsRepository.save(posts);
}
}
Let me explain this code a little bit:
We wanted to create 17 posts, so we make an array of 17 items first and fill it with empty strings. It is important to fill our dynamic array with fill()
otherwise the next call of map()
won't work because the array is still an array of undefined items.
Then, we call the map()
method inside of which we generate random posts with the method make()
. That method does not save any records in the database, it only generates an entity instance and it accepts custom properties as parameters. For example, in our post factory, we omitted the assignment of the author field on purpose and it is here that we want to assign an author to our post. And we do so randomly with the helper function arrayElement
from faker which returns a random element from a given array: in our case from the previously generated users.
We wrapped everything just discussed above in Promise.all()
to take advantage of node.js asynchronous abilities at the most and await the result in the posts
variable (or constant). Remember, those posts are not yet saved since we only used to call make()
before.
Finally, It's time to save them on the last line by the mean of the posts repository.
Time to run the seeds
Create a file seed.ts
and write down inside it the following code:
import "reflect-metadata";
import { DataSource, DataSourceOptions } from "typeorm";
import { runSeeders, SeederOptions } from "typeorm-extension";
import { User } from "./users.entity";
import { Post } from "./posts.entity";
import { UsersFactory } from "./users.factory";
import { PostsFactory } from "./users.factory";
import { MainSeeder } from "./main.seeder";
const {
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME,
} = process.env;
const options: DataSourceOptions & SeederOptions = {
type: "mysql",
host: DB_HOST || "localhost",
port: Number(DB_PORT) || 3306,
username: DB_USER || "test",
password: DB_PASSWORD || "test",
database: DB_NAME || "test",
entities: [User, Post],
// additional config options brought by typeorm-extension
factories: [UsersFactory, PostsFactory],
seeds: [MainSeeder],
};
const dataSource = new DataSource(options);
dataSource.initialize().then(async () => {
await dataSource.synchronize(true);
await runSeeders(dataSource);
process.exit();
});
I assume that you are already familiar with TypeORM's data source configuration and initialization. The only new properties there are factories
and seeds
which are brought by typeorm-extension. Hence as their names suggest, they are pretty straightforward to understand.
Next to the initialization, we tell TypeORM to synchronize the database with a call to synchronize()
method and especially, we drop the previous data by giving it an argument true
.
Finally, we run the seeds with the runSeeders()
function imported from typeorm-extension and then exits the process.
We want to be able to run the seeds as a run-script in our project, so in our package.json amend the the following line in the scripts field:
{
//...
"scripts": {
//...
"seed": "ts-node src/seeds.ts" // or whatever path to your seed file
},
// ...
}
Then install the package ts-node
:
yarn add -D ts-node
Now you are going to be able to seed your database with the command:
yarn seed
Afterwords
The package typeorm-extension is just great, and I have not expanded its full specs in this post. What I've shown you here is just an opinionated approach to how to leverage its power. So, I'd suggest you give an eye on its documentation here.
Happy hacking and happy seeding!!
Subscribe to my newsletter
Read articles from Andrianarisoa Daniel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Andrianarisoa Daniel
Andrianarisoa Daniel
I !drink a lot of coffee😛