Learning how to seed data with typeorm-seeding
當你在進行專案開發(Development)時,通常資料庫裡面都需要一些進行測試用的假資料(Fake data);或者,專案上線(Production)時,會需要有一些 metadata
在裡面。例如:角色(Roles)、權限(Permissions)...等等。
此時,你有兩種方式餵資料給資料庫。
第一種,手動新增(Manually);第二種,寫程式自動化(Automatically)新增,也就是我想介紹給大家的方式。
然而,寫程式自動化新增也有很多途徑,如果有上網做過功課的,看見最多的教學文件應該是使用 migration
。由於,跟 migration
相關的教學文章(如:How to seed TypeORM)已經有太多了,我就不多加介紹。在這邊我想介紹另一種方式,使用 typeorm-seeding
和 faker
這兩個套件,幫助我們餵資料給資料庫。
Faker 是一個幫助我們產生假資料的套件,有許多方法可以使用。
例如:faker.name.lastName()
,faker.internet.email()
,faker.phone.phoneNumber()
Steps
- Install
typeorm-seeding
,@types/faker
(Optional, if you're using TypeScript) - Setup
seeds
andfactories
- Run seeder script with
ormconfig
這邊假設,你已經有 Entity 了。 例如:
user.entity.ts
,pet.entity.ts
,role.entity.ts
Installation
npm i typeorm-seeding# oryarn add typeorm-seeding
Optional, if you’re using TypeScript
npm install -D @types/faker
We don’t need to install
faker
manually, because it's a dependency astypeorm-seeding
.
What do we need?
- Seeds
- Factories
It is important that naming these files suffixed with .seed.ts
and .factory.ts
例如:src/database/factories/user.factory.ts
Build our first Factory
src/database/factories/user.factory.ts
我們使用 typeorm-seeding 提供的方法 define
定義 Factory
。
Factory 的主要目的是:利用資料(fake data)來產生實體(Entity)。
第一個參數為:Entity 第二個參數為:FactoryFunction,接收 faker
和 context
這兩個參數。
faker:主要用來產生假資料。
context:則是呼叫此 factory 時,用來接收傳遞下來的參數。在這個範例中,我並沒有用到。
export enum Gender { MALE = 'MALE', FEMALE = 'FEMALE', DIVERSE = 'DIVERSE',}import Faker from 'faker';import { define } from 'typeorm-seeding';import { User } from '../../users/user.entity';import { Gender } from '../../users/gender.enum';define(User, (faker: typeof Faker) => {const firstName = faker.name.firstName();const lastName = faker.name.lastName();const email = faker.internet.exampleEmail(firstName, lastName);const phone = faker.phone.phoneNumber();const address = faker.address.streetAddress();const dateOfBirth = faker.date.past();const gender = faker.random.objectElement<Gender>(Gender);const user = new User(); user.firstName = firstName; user.lastName = lastName; user.gender = gender; user.email = email; user.phone = phone; user.address = address; user.dateOfBirth = dateOfBirth; return user;});
Create our CreateUsers Seeder
src/database/seeds/create-user.seed.ts
有了 Factory
之後,我們可以來建立我們的 Seeder
了。 Seeder 是我們利用 factory 產生假資料,並寫入資料庫的地方。
首先,我們需要將我們的 CreateUser Seeder 用 implements
實踐來自 typeorm-seeding 的 Seeder
。 接著我們就可以使用 Seeder 預設提供的一個 run
方法(method),和 define
一樣,提供兩個參數。
第一個參數為:Factory,透過呼叫 factory()() 來產生實體(Entity)並取得 EntityFactory。(HOF 用法)
第二個參數為:Connection,來自 typeorm 的 Connection。在這個範例中,我並沒有用到。
factory:第一個 () 接收 Entity,第二個 () 接收自定義的內容,自定義的內容會被傳送到 Factory 的第二個參數 context 中。
例如:factory(User)('Hi, my name is Aaron')
import { Factory, Seeder } from 'typeorm-seeding';import { User } from '../../users/user.entity';export default class CreateUsers implements Seeder { public async run(factory: Factory): Promise<void> { await factory(User)().createMany(10); }}
當你有多個 seed 檔案時,預設執行順序將按照字母順序執行。
Entity Factory
Entity Factory 透過執行 factory(User)()
後取得,他是一個已經跟 Entity
結合的 Factory
,可以用來將實體寫入資料庫。
提供了幾個方法(method),例如:map
, create
, createMany
, make
, makeMany
。
const userEntityFactory = factory(User)();await userEntityFactory.create();await userEntityFactory.createMany(10);await userEntityFactory.make();await userEntityFactory.makeMany(10);
create
與 make
最大的差別在於,create
會將資料寫入資料庫,而 make
不會。
詳細用法,請查閱:EntityFactory
Seed data to our database
當我們的 Factory
與 Seeder
都設定好了之後,我們就可以餵資料到資料庫了!
首先,我們需要告訴 typeorm-seeder
,我們的 seeds 跟 factories 在哪裡,在你的 ormconfig.js
加入...
ormconfig.js
module.exports = { ... seeds: ['src/seeds/**/*{.ts,.js}'], factories: ['src/factories/**/*{.ts,.js}'],}
接著,就可以執行 seed 的指令了。為了方便我們將指令加入 scripts 中。
package.json
"scripts": { "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config" "seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed"...}
Run the script to seed data
最後就可以用程式自動化餵資料到資料庫啦~🔥
npm run seed:run# oryarn seed:run
Caution
預設你的 ormconfig.js
在專案的根目錄中,而且名稱為 ormconfig.js
如果路徑與檔名不一樣,則可以透過 --configName (or -n)
與 --root (or -r)
,指定檔案位置與檔案名稱。
假設你的 ormconfig.js
放在 src/config/ormconfig.js
,則需設定為...
-n ./src/config/ormconfig.js
"scripts": { "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config -n ./src/config/ormconfig.js", "seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed -n ./src/config/ormconfig.js"...}
經測試過後發現--root
跟想像中的設定有落差。若使用 -r
則會噴以下錯誤。
-r ./src/config (Don’t do this!)
"scripts": { "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config -r ./src/config", "seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed -r ./src/config"...}