{"id":17652391,"url":"https://github.com/bashleigh/typeorm-polymorphic","last_synced_at":"2025-05-15T15:06:59.542Z","repository":{"id":40431046,"uuid":"252190955","full_name":"bashleigh/typeorm-polymorphic","owner":"bashleigh","description":"Typeorm polymorphic relationship management","archived":false,"fork":false,"pushed_at":"2025-03-08T20:25:44.000Z","size":491,"stargazers_count":205,"open_issues_count":19,"forks_count":42,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-14T14:35:30.324Z","etag":null,"topics":["polymorphic","typeorm"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bashleigh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-01T14:00:27.000Z","updated_at":"2025-05-10T18:18:27.000Z","dependencies_parsed_at":"2024-06-18T18:28:41.843Z","dependency_job_id":"49df1bb6-1577-45a8-9d22-19a7805209ab","html_url":"https://github.com/bashleigh/typeorm-polymorphic","commit_stats":{"total_commits":82,"total_committers":5,"mean_commits":16.4,"dds":"0.060975609756097615","last_synced_commit":"2e996ec139329dd6a6581b9937b6e6d06a972dfb"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashleigh%2Ftypeorm-polymorphic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashleigh%2Ftypeorm-polymorphic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashleigh%2Ftypeorm-polymorphic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashleigh%2Ftypeorm-polymorphic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bashleigh","download_url":"https://codeload.github.com/bashleigh/typeorm-polymorphic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["polymorphic","typeorm"],"created_at":"2024-10-23T11:46:44.611Z","updated_at":"2025-05-15T15:06:56.233Z","avatar_url":"https://github.com/bashleigh.png","language":"TypeScript","readme":"# typeorm-polymorphic\n\u003ca href=\"https://www.npmjs.com/package/typeorm-polymorphic\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/typeorm-polymorphic.svg\"/\u003e\u003c/a\u003e\n\u003cimg src=\"https://github.com/bashleigh/typeorm-polymorphic/workflows/Tests/badge.svg\"/\u003e\n\u003cimg src=\"https://camo.githubusercontent.com/a34cfbf37ba6848362bf2bee0f3915c2e38b1cc1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265\" /\u003e\n[![Coverage Status](https://coveralls.io/repos/github/bashleigh/typeorm-polymorphic/badge.svg?branch=master)](https://coveralls.io/github/bashleigh/typeorm-polymorphic?branch=master)\n\nAn extension package for polymorphic relationship management, declaration and repository queries for [typeorm](https://typeorm.io/)\n\n\u003e Experiemental package\n\n## Install \n\n```bash\n$ yarn add typeorm-polymorphic\n```\n\n\u003e You'll also require `typeorm` and `reflect-metadata` if you haven't already installed these\n\nThis is a concept I've put together for decorated polymorphic values with typeorm. I've taken a lot of inspiration from laravel's eloquent.\n\nThis has worked for my use case however it might not for others. This is an example of how I've used it.\n\n### Extend the PolymorphicRepository\n\n```ts\nimport { PolymorphicRepository } from 'typeorm-polymorphic';\n\n@PolymorphicRepository(AdvertEntity)\nexport class AdvertRepository extends AbstractPolymorphicRepository\u003c\n  AdvertEntity\n\u003e {}\n```\n\nThen, to instantiate your repository you can call:\n\n```ts\nimport { AbstractPolymorphicRepository } from 'typeorm-polymorphic';\n\nconst repository = AbstractPolymorphicRepository.createRepository(\n  dataSource, // where `dataSource` is a typeorm DataSource object\n  AdvertRepository,\n);\n```\n\n\u003e The below decorators will only work when using the above abstract repository AbstractPolymorphicRepository\n\n### Setup the entities\n\nThis is an example of one child, 2 parent types\n\n#### Parents\n\n```ts\n@Entity('users')\nexport class UserEntity {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @PolymorphicChildren(() =\u003e AdvertEntity, {\n    eager: false,\n  })\n  adverts: AdvertEntity[];\n}\n```\n```ts\nEntity('merchants')\nexport class MerchantEntity {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @PolymorphicChildren(() =\u003e AdvertEntity, {\n    eager: false,\n  })\n  adverts: AdvertEntity[];\n}\n```\n\n#### Children\n\n```ts\n@Entity('adverts') \nexport class AdvertEntity implements PolymorphicChildInterface {\n  @PolymorphicParent(() =\u003e [UserEntity, MerchantEntity])\n  owner: UserEntity | MerchantEntity;\n\n  @Column()\n  entityId: number;\n\n  @Column()\n  entityType: string;\n}\n```\n\n#### Resulting values\n\nThis will result in the adverts table having values \n\n```\nadverts table\n==========================\nid | entityId | entityType\n==========================\n 1 | 1        | 'UserEntity'\n 2 | 1        | 'MerchantEntity'\n 3 | 2        | 'UserEntity'\n```\n\n## Decorators\n\nBoth `PolymorphicChildren` and `PolymorphicParent` are treated same. Currently some of the default values are different but eventually these method should be synonyms of one another. They have different names because it helped me describe the relationship directions which could be explained as 'parent' 'child' in different ways.\n\n`PolymorphicRepository` allows you to define a custom typeorm repository and then\ninstantiate it later via `AbstractPolymorphicRepository.createRepository(...)`.\n\n### Ambiguous direction\n\nBoth `PolymorphicParent` and `PolymorphicChildren` accepts either an array of types or a singular type\n\n```ts\n@PolymorphicChildren(() =\u003e [ChildEntity, AnotherChildEntity])\n@PolymorphicParent(() =\u003e [ParentEntity, AnotherParentEntity])\n\n@PolymorphicChildren(() =\u003e ChildEntity)\n@PolymorphicParent(() =\u003e ParentEntity)\n```\n\n### Options\n\nkey | what's it for? | default\n---|---|---\neager | load relationships by default | true\ncascade | save/delete parent/children on save/delete | true\ndeleteBeforeUpdate | delete relation/relations before update | false\nhasMany | should return as array? | true for child. false for parent\n\n\u003e hasMany should really be updated so both parent and child declaration are the same. I've done to hopefully avoid confusion from the names!\n\n## Repository Methods \n\nThe majority of these methods overwrite the typeorm's `Repository` class methods to ensure polymorph relationships are handled before/after the parent's method.\n\n### save\n\nSaves the given entity and it's parent or children\n\nextends typeorm's Repository.save method\n\n##### Child\n\n```ts\nconst repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository\n\nconst advert = new AdvertEntity();\nadvert.owner = user;\n\nawait repository.save(advert);\n```\n\n##### Parent\n\n```ts\nconst repository = connection.getRepository(MerchantRepository); // That extends AbstractPolymorphicRepository\n\nconst advert = new AdvertEntity();\n\nconst merchant = new MerchantEntity();\nmerchant.adverts = [advert];\n\nawait repository.save(merchant);\n```\n\n### find\n\nextends typeorm's Repository.find method\n\n```ts\nconst repository = connection.getRepository(MerchantRepository); // That extends AbstractPolymorphicRepository\n\nconst results = await repository.find();\n\n// results[0].adverts === AdvertEntity[]\n```\n### findOne\n\nextends typeorm's Repository.findOne method\n\n\n### create\n\nThis method creates the parent or child relations for you so you don't have to manally supply an array of classes.\n\nextends typeorm's Repository.create method\n\n##### Child\n\n```ts\nconst repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository\n\nconst results = await repository.create({\n  owner: new UserEntity(), // or MerchantEntity()\n});\n```\n\n##### Parent\n\n```ts\nconst repository = connection.getRepository(UserRepository); // That extends AbstractPolymorphicRepository\n\nconst results = await repository.create({\n  adverts: [\n    {\n      name: 'test',\n    },\n    {\n      name: 'test',\n    },\n  ],\n});\n\n/**\n * {\n *   adverts: [\n *     AdvertEntity{\n *       name: 'test',\n *     },\n *     AdvertEntity{\n *       name: 'test',\n *     },\n *   ],\n * }\n*/\n```\n\n### hydrateMany\n\nHydreate one entity and get their relations to parent/child\n\n```ts\nconst repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository\n\nconst adverts = await repository.find();\n// eager to parent (user|merchant) is set to false\nadverts[0].owner; // undefined\n\nawait repository.hydrateMany(adverts);\n\nadverts[0].owner; // UserEntity | MerchantEntity\n```\n\n### hydrateOne\n\nHydreate one entity and get their relations to parent/child\n\n```ts\nconst repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository\n\nconst advert = await repository.findOne(1);\n// eager to parent (user|merchant) is set to false\nadvert.owner; // undefined\n\nawait repository.hydrateOne(advert);\n\nadvert.owner; // UserEntity | MerchantEntity\n```\n\n## Class-transformer\n\nWe recommend if you're working with polymorphic relationships that you use `class-transformers`'s `Transform` decorator to distinguish the different types on the frontend when returning your entities from a http call\n\n```ts\n@Entity('adverts') \nexport class AdvertEntity implements PolymorphicChildInterface {\n  @PolymorphicParent(() =\u003e [UserEntity, MerchantEntity])\n  @Transform(\n    (value: UserEntity | MerchantEntity) =\u003e ({\n      ...value,\n      type: value.constructor.name,\n    }),\n    {\n      toPlainOnly: true,\n    },\n  )\n  owner: UserEntity | MerchantEntity;\n\n  @Column()\n  entityId: number;\n\n  @Column()\n  entityType: string;\n}\n```\n\nThe owner property object's type property will now either be string value of `UserEntity` or `MerchantEntity`\n\n\n## Possible relations \n\n### Singular parent, different children\n\nThis is an example of having the need of different types of children for a singular parent type\n\n```ts\nclass RestaurantEntity {\n  @PolymorphicChildren(() =\u003e [WaiterEntity, ChefEntity])\n  staff: (WaiterEntity | ChefEntity)[];\n}\n\nclass WaiterEntity implements PolymorphicChildInterface {\n  @Column()\n  entityId: string;\n\n  @Column()\n  entityType: string;\n\n  @PolymorphicParent(() =\u003e RestaurantEntity)\n  restaurant: RestaurantEntity;\n}\n\nclass ChefEntity implements PolymorphicChildInterface {\n  @Column()\n  entityId: string;\n\n  @Column()\n  entityType: string;\n\n  @PolymorphicParent(() =\u003e RestaurantEntity)\n  restaurant: RestaurantEntity;\n}\n\n```\n\n### Singular child, different parent\n\nThis is an example of having the need of a singular child shared between different types of parents\n\n```ts\nclass AdvertEntity implements PolymorphicChildInterface {\n  @PolymorphicParent(() =\u003e [UserEntity, MerchantEntity])\n  owner: UserEntity | MerchantEntity;\n}\n\nclass MerchantEntity {\n  @PolymorphicChildren(() =\u003e AdvertEntity)\n  adverts: AdvertEntity[];\n}\n\nclass UserEntity {\n  @PolymorphicChildren(() =\u003e AdvertEntity)\n  adverts: AdvertEntity[];\n}\n\n```\n\n## Notes\n\nI think [Perf](https://github.com/Perf) might have some suggestions on how to improve things (sorry I have replied been mega busy!)\n\n## Nestjs \n\nIf you're using nestjs, don't forgot to include your repository into the entities array in forFeature\n\n```ts\n@Module({\n  imports: [\n    TypeOrmModule.forFeature([\n      AdvertEntity,\n      AdvertRepository,\n    ]),\n  ],\n  providers: [AdvertService, CategoryService, TagService, AdvertPolicy],\n  exports: [TypeOrmModule, AdvertService],\n})\nexport class AdvertModule {}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbashleigh%2Ftypeorm-polymorphic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbashleigh%2Ftypeorm-polymorphic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbashleigh%2Ftypeorm-polymorphic/lists"}