Coder Social home page Coder Social logo

jedliu / typeorm-seeding Goto Github PK

View Code? Open in Web Editor NEW

This project forked from jorgebodega/typeorm-seeding

0.0 0.0 0.0 5.51 MB

๐ŸŒฑ A delightful way to seed test data into your database.

Home Page: https://www.npmjs.com/package/@jorgebodega/typeorm-seeding

License: MIT License

JavaScript 2.45% TypeScript 97.55%

typeorm-seeding's Introduction

logo

TypeORM Seeding

NPM NPM latest version NPM next version Semantic release

Coveralls master branch Coveralls next branch

Checks for master branch Checks for next branch

A delightful way to seed test data into your database.
Inspired by the awesome framework laravel in PHP, MikroORM seeding and the repositories from pleerock

Made with โค๏ธ by Gery Hirschfeld, Jorge Bodega and contributors


Contents

Installation

Before using this TypeORM extension please read the TypeORM Getting Started documentation. This explains how to setup a TypeORM project.

After that install the extension with npm or yarn. Add development flag if you are not using seeders nor factories in production code.

npm i [-D] @jorgebodega/typeorm-seeding
yarn add [-D] @jorgebodega/typeorm-seeding

Configuration

To configure the path to your seeders extends the TypeORM config file or use environment variables like TypeORM. If both are used the environment variables will be prioritized.

ormconfig.js

module.exports = {
  ...
  seeders: ['src/seeds/**/*{.ts,.js}'],
  defaultSeeder: RootSeeder,
  ...
}

.env

TYPEORM_SEEDING_SEEDERS=src/seeds/**/*{.ts,.js}
TYPEORM_SEEDING_DEFAULT_SEEDER=RootSeeder

Introduction

Isn't it exhausting to create some sample data for your database, well this time is over!

How does it work? Just create a entity factory and/or seed script.

Entity

@Entity()
class User {
  @PrimaryGeneratedColumn('increment')
  id!: number

  @Column()
  name!: string

  @Column()
  lastName!: string

  @Column()
  email!: string

  @OneToMany(() => Pet, (pet) => pet.owner)
  pets?: Pet[]

  @ManyToOne(() => Country, (country) => country.users, { nullable: false })
  @JoinColumn()
  country!: Country
}

Factory

class UserFactory extends Factory<User> {
  protected entity = User
  protected attrs: FactorizedAttrs<User> = {
    name: faker.name.firstName(),
    lastName: async () => faker.name.lastName(),
    email: new InstanceAttribute((instance) =>
      [instance.name.toLowerCase(), instance.lastName.toLowerCase(), '@email.com'].join(''),
    ),
    country: new Subfactory(CountryFactory),
  }
}

Seeder

class UserSeeder extends Seeder {
  async run(connection: Connection) {
    await new UserFactory().createMany(10)

    await this.call(connection, [PetSeeder])
  }
}

Factory

Factory is how we provide a way to simplify entities creation, implementing a factory creational pattern. It is defined as an abstract class with generic typing, so you have to extend over it.

class UserFactory extends Factory<User> {
  protected entity = User
  protected attrs: FactorizedAttrs<User> = {
    ...
  }
}

attrs

Attributes objects are superset from the original entity attributes.

protected attrs: FactorizedAttrs<User> = {
  name: faker.name.firstName(),
  lastName: async () => faker.name.lastName(),
  email: new InstanceAttribute((instance) =>
    [instance.name.toLowerCase(), instance.lastName.toLowerCase(), '@email.com'].join(''),
  ),
  country: new Subfactory(CountryFactory),
}

Those factorized attributes resolves to the value of the original attribute, and could be one of the following types:

Simple value

Nothing special, just a value with same type.

protected attrs: FactorizedAttrs<User> = {
  name: faker.name.firstName(),
}

Function

Function that could be sync or async, and return a value of the same type. This function will be executed once per entity.

protected attrs: FactorizedAttrs<User> = {
  lastName: async () => faker.name.lastName(),
}

InstanceAttribute

class InstanceAttribute<T, V> {
  constructor(private callback: (entity: T) => V) {}

  ...
}

Class with a function that receive the current instance and returns a value of the same type. It is ideal for attributes that could depend on some others to be computed.

Will be executed after the entity has been created and the rest of the attributes have been calculated, but before persistance (in case of create or createMany).

protected attrs: FactorizedAttrs<User> = {
  name: faker.name.firstName(),
  lastName: async () => faker.name.lastName(),
  email: new InstanceAttribute((instance) =>
    [instance.name.toLowerCase(), instance.lastName.toLowerCase(), '@email.com'].join(''),
  ),
}

In this simple case, if name or lastName override the value in any way, the email attribute will be affected too.

LazyInstanceAttribute

class LazyInstanceAttribute<T, V> {
  constructor(private callback: (entity: T) => V) {}

  ...
}

Class with similar functionality than InstanceAttribute, but it will be executed only after persistance. This is useful for attributes that depends on the database id, like relations.

Just remember that, if you use make or makeMany, the only difference between InstanceAttribute and LazyInstanceAttribute is that LazyInstanceAttribute will be processed the last.

protected attrs: FactorizedAttrs<User> = {
  name: faker.name.firstName(),
  email: new LazyInstanceAttribute((instance) =>
    [instance.name.toLowerCase(), instance.id, '@email.com'].join(''),
  ),
}

Subfactory

export class Subfactory<T> {
  constructor(factory: Constructable<Factory<T>>)
  constructor(factory: Constructable<Factory<T>>, values?: Partial<FactorizedAttrs<T>>)
  constructor(factory: Constructable<Factory<T>>, count?: number)
  constructor(factory: Constructable<Factory<T>>, values?: Partial<FactorizedAttrs<T>>, count?: number)

  ...
}

Subfactories are just a wrapper of another factory, to avoid explicit operations that could lead to unexpected results over that factory, like

protected attrs: FactorizedAttrs<User> = {
  country: async () => new CountryFactory().create({
    name: faker.address.country(),
  }),
}

instead of

protected attrs: FactorizedAttrs<User> = {
  country: new Subfactory(CountryFactory, {
    name: faker.address.country(),
  }),
}

Subfactory just execute the same kind of operation (make or create) over the factory. If count param is provided, it will execute makeMany/createMany instead of make/create, and returns an array.

make & makeMany

Make and makeMany executes the factory functions and return a new instance of the given entity. The instance is filled with the generated values from the factory function, but not saved in the database.

  • overrideParams - Override some of the attributes of the entity.
make(overrideParams: Partial<FactorizedAttrs<T>> = {}): Promise<T>
makeMany(amount: number, overrideParams: Partial<FactorizedAttrs<T>> = {}): Promise<T[]>
new UserFactory().make()
new UserFactory().makeMany(10)

// override the email
new UserFactory().make({ email: '[email protected]' })
new UserFactory().makeMany(10, { email: '[email protected]' })

create & createMany

the create and createMany method is similar to the make and makeMany method, but at the end the created entity instance gets persisted in the database using TypeORM entity manager.

  • overrideParams - Override some of the attributes of the entity.
  • saveOptions - Save options from TypeORM
create(overrideParams: Partial<FactorizedAttrs<T>> = {}, saveOptions?: SaveOptions): Promise<T>
createMany(amount: number, overrideParams: Partial<FactorizedAttrs<T>> = {}, saveOptions?: SaveOptions): Promise<T[]>
new UserFactory().create()
new UserFactory().createMany(10)

// override the email
new UserFactory().create({ email: '[email protected]' })
new UserFactory().createMany(10, { email: '[email protected]' })

// using save options
new UserFactory().create({ email: '[email protected]' }, { listeners: false })
new UserFactory().createMany(10, { email: '[email protected]' }, { listeners: false })

faker

Faker package has been removed from dependencies. If you want to use it, please install it manually and just import when needed.

npm i [-D] @faker-js/faker
yarn add [-D] @faker-js/faker
import { faker } from '@faker-js/faker'

class UserFactory extends Factory<User> {
  ...
}

Seeder

Seeder class is how we provide a way to insert data into databases, and could be executed by the command line or by helper method. Is an abstract class with one method to be implemented, and a helper function to run some more seeder sequentially.

class UserSeeder extends Seeder {
  async run(connection: Connection) {
    ...
  }
}

run

This function is the one that needs to be defined when extending the class. Could use call to run some other seeders.

run(connection: Connection): Promise<void>
async run(connection: Connection) {
    await new UserFactory().createMany(10)

    await this.call(connection, [PetSeeder])
}

call

This function allow to run some other seeders in a sequential way.

In order to use seeders from cli command, a default seeder class must be provided as root seeder, working as a tree structure.

logo

CLI Configuration

There are two possible commands to execute, one to see the current configuration and one to run a seeder.

Add the following scripts to your package.json file to configure them.

"scripts": {
  "seed:config": "typeorm-seeding config",
  "seed:run": "typeorm-seeding seed",
  ...
}

config

This command just print the connection configuration.

typeorm-seeding config

Example result

{
  "name": "default",
  "type": "sqlite",
  "database": "/home/jorgebodega/projects/typeorm-seeding/test.db",
  "entities": ["sample/entities/**/*{.ts,.js}"],
  "seeders": ["sample/seeders/**/*{.ts,.js}"],
  "defaultSeeder": "RootSeeder"
}
Options
Option Default Description
--seed or -s null Option to specify a seeder class to run individually. (Only on seed command)
--connection or -c TypeORM default value Name of the TypeORM connection. Required if there are multiple connections.
--configName or -n TypeORM default value Name to the TypeORM config file.
--root or -r TypeORM default value Path to the TypeORM config file.

seed

This command execute a seeder, that could be specified as a parameter.

typeorm-seeding seed

The name of the seeder to execute (either set with the --seed option or with default in configs) must be the seeder's class name, and thus, the seeder must be exported with a named export. Please avoid default export for seeders: it may imply unwanted behavior. (See #75).

Options
Option Default Description
--seed or -s Default seeder specified config file Option to specify a seeder class to run individually.
--connection or -c TypeORM default value Name of the TypeORM connection. Required if there are multiple connections.
--configName or -n TypeORM default value Name to the TypeORM config file.
--root or -r TypeORM default value Path to the TypeORM config file.

Testing features

We provide some testing features that we already use to test this package, like connection configuration. The entity factories can also be used in testing. To do so call the useFactories or useSeeders function.

useSeeders

Execute one or more seeders.

useSeeders(entrySeeders: ClassConstructor<Seeder> | ClassConstructor<Seeder>[]): Promise<void>
useSeeders(
  entrySeeders: ClassConstructor<Seeder> | ClassConstructor<Seeder>[],
  customOptions: Partial<ConnectionConfiguration>,
): Promise<void>

typeorm-seeding's People

Contributors

adrien-may avatar buuug7 avatar clovis1122 avatar davicedraz avatar dennisameling avatar dependabot[bot] avatar eugenio165 avatar jesster2k10 avatar jleck avatar jorgebodega avatar jsouto18 avatar jthodge avatar mkorobkov avatar oroce avatar owonwo avatar raphaelwoude avatar renovate-bot avatar renovate[bot] avatar semantic-release-bot avatar sfelix-martins avatar stephane-rbn avatar vologab avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.