{"id":21300909,"url":"https://github.com/kibae/typeorm-auditing","last_synced_at":"2025-07-11T20:31:04.137Z","repository":{"id":47437661,"uuid":"516104892","full_name":"kibae/typeorm-auditing","owner":"kibae","description":"TypeORM Auditing: Create history tables and manage changes of entity automatically.","archived":false,"fork":false,"pushed_at":"2023-01-14T02:52:38.000Z","size":149,"stargazers_count":27,"open_issues_count":10,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-06T08:34:35.567Z","etag":null,"topics":["auditing","decorator","nodejs","orm","typeorm","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/typeorm-auditing","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/kibae.png","metadata":{"files":{"readme":"README.ko.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-20T19:09:29.000Z","updated_at":"2025-03-07T04:51:51.000Z","dependencies_parsed_at":"2023-02-09T18:46:13.668Z","dependency_job_id":null,"html_url":"https://github.com/kibae/typeorm-auditing","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/kibae/typeorm-auditing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kibae%2Ftypeorm-auditing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kibae%2Ftypeorm-auditing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kibae%2Ftypeorm-auditing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kibae%2Ftypeorm-auditing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kibae","download_url":"https://codeload.github.com/kibae/typeorm-auditing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kibae%2Ftypeorm-auditing/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264892073,"owners_count":23679219,"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":["auditing","decorator","nodejs","orm","typeorm","typescript"],"created_at":"2024-11-21T15:42:14.608Z","updated_at":"2025-07-11T20:31:01.846Z","avatar_url":"https://github.com/kibae.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TypeORM Auditing(Decorator)\n- Entity의 변화(Create, Update, Delete)를 자동으로 구성된 히스토리 테이블에 적재합니다.\n\n[![Node.js CI](https://github.com/kibae/typeorm-auditing/actions/workflows/node.js.yml/badge.svg)](https://github.com/kibae/typeorm-auditing/actions/workflows/node.js.yml)\n[![NPM Version](https://badge.fury.io/js/typeorm-auditing.svg)](https://www.npmjs.com/package/typeorm-auditing)\n[![License](https://img.shields.io/github/license/kibae/typeorm-auditing)](https://github.com/kibae/typeorm-auditing/blob/main/LICENSE)\n\n## Install\n- NPM\n```shell\n$ npm install typeorm-auditing --save\n```\n\n- Yarn\n```shell\n$ yarn add typeorm-auditing\n```\n\n----\n\n## Usage\n### 0. Origin entity *(기존 코드를 바꿀 필요가 없습니다)*\n- 기존의 entity 정의를 그대로 활용할 수 있습니다.\n```typescript\nexport abstract class MyBase extends BaseEntity {\n    @PrimaryGeneratedColumn()\n    id: number;\n}\n\n@Entity()\n// @Entity({ ...yourEntityOption, database: 'my-database', schema: 'my-schema' })\nexport class User extends MyBase {\n    @Column()\n    firstName: string;\n    @Column()\n    lastName: string;\n    @Column()\n    age: number;\n\n    public get fullName(): string {\n        return `${this.firstName} ${this.lastName}`;\n    }\n}\n```\n\n### 1. Auditing Entity\n- 일반적인 entity를 정의하듯이 히스토리가 적재될 테이블을 정의할 수 있습니다.\n  - 이 예제는 편의를 위해 origin entity를 상속했습니다. Origin을 상속하지 않고 별도(TypeORM.ObjectLiteral)로 entity를 구성해도 됩니다.\n  - @Entity decorator 대신 **@AuditingEntity(*TargetEntity*)** decorator를 사용합니다.\n  - 자동으로 **_seq**, **_action(*Create, Update, Delete*)**, **_modifiedAt** 컬럼을 추가됩니다. public getter를 정의하여 원하는 이름으로 활용할 수 있습니다.  \n```typescript\nimport { AuditingAction, AuditingEntity, AuditingEntityDefaultColumns } from 'typeorm-auditing'; \n\n@AuditingEntity(User)\n// @AuditingEntity(User, { ...overrideUserEntitiesEntityOption, database: 'my-database', schema: 'my-schema' })\nexport class AuditingUser extends User implements AuditingEntityDefaultColumns {\n    readonly _seq: number;\n    readonly _action: AuditingAction;\n    readonly _modifiedAt: Date;\n\n    // 일반적인 entity처럼 컬럼을 추가하고 인덱스도 설정할 수 있습니다. 일반적으로는 TypeORM event를 통해 자동으로 레코드가 생성되기 때문에 nullable이거나 @BeforeInsert를 활용하여 내용을 채워줘야 합니다.\n    // @Column({ nullable: true })\n    // additionalColumn!: string;\n}\n```\n\n### 2. Subscribe TypeORM event\n- TypeORM DataSource마다 subscribers를 설정해야 합니다. 하나의 DataSource에 한 번만 설정하면 되지만, 여러 DataSource가 정의된다면 각각 설정해 주세요.\n```typescript\nimport { AuditingSubscriber } from 'typeorm-auditing';\n\nconst dataSource = new DataSource({\n    ...yourDataSourceConfig,\n    entities: [User, AuditingUser],\n    //entities: ['path-of-your-entities-path'],\n    subscribers: [AuditingSubscriber],\n})\n```\n\n----\n\n## Contributors\n\u003ca href=\"https://github.com/kibae/typeorm-auditing/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=kibae/typeorm-auditing\" /\u003e\n\u003c/a\u003e\n\n----\n\n## Example\n\n### [Sample Code](https://github.com/kibae/typeorm-auditing/blob/main/src/test/auditing-entity.decorator.example.ts)\n```typescript\nimport { DataSource } from 'typeorm';\nimport { Case1, Case1Audit } from './entity/case1';\nimport { AuditingSubscriber } from 'typeorm-auditing';\n\n(async function () {\n    //Data Source\n    const dataSource = await new DataSource({\n        type: 'sqlite',\n        database: ':memory:',\n        synchronize: true,\n        logging: 'all',\n        entities: [Case1, Case1Audit],\n        subscribers: [AuditingSubscriber],\n    }).initialize();\n\n    //Create\n    const entity = await dataSource.manager.save(\n        Case1.create({\n            firstName: 'Timber',\n            lastName: 'Saw',\n            age: 25,\n        })\n    );\n\n    //Update\n    entity.age++;\n    await entity.save();\n\n    //Delete\n    await entity.remove();\n\n    //!!!! Print history entities\n    console.log(await dataSource.manager.find(Case1Audit));\n})();\n```\n\n### [Sample Entity](https://github.com/kibae/typeorm-auditing/blob/main/src/test/entity/case1.ts)\n- Origin Entity\n```typescript\nimport { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity()\nexport class Case1 extends BaseEntity {\n    @PrimaryGeneratedColumn({ type: 'int' })\n    id!: number;\n\n    @Column()\n    firstName!: string;\n\n    @Column()\n    lastName!: string;\n\n    @Column()\n    age!: number;\n}\n```\n\n- Audit Entity\n```typescript\nimport { AuditingAction, AuditingEntity, AuditingEntityDefaultColumns } from 'typeorm-auditing';\n\n@AuditingEntity(Case1)\nexport class Case1Audit extends Case1 implements AuditingEntityDefaultColumns {\n    readonly _seq!: number;\n    readonly _action!: AuditingAction;\n    readonly _modifiedAt!: Date;\n}\n```\n\n### Result\n\n```shell\n$ npx ts-node src/test/auditing-entity.decorator.example.ts \ncreating a new table: case1\nquery: CREATE TABLE \"case1\" (\"id\" integer PRIMARY KEY AUTOINCREMENT NOT NULL, \"firstName\" varchar NOT NULL, \"lastName\" varchar NOT NULL, \"age\" integer NOT NULL)\nquery: CREATE TABLE \"case1_audit\" (\"_seq\" integer PRIMARY KEY AUTOINCREMENT NOT NULL, \"_action\" varchar(20) NOT NULL, \"_modifiedAt\" datetime NOT NULL DEFAULT (datetime('now')), \"id\" integer, \"firstName\" varchar, \"lastName\" varchar, \"age\" integer)\nquery: CREATE INDEX \"IDX_d2fc2ad0a4f22955513bca9b1d\" ON \"case1_audit\" (\"id\") \nquery: COMMIT\n\nquery: BEGIN TRANSACTION\nquery: INSERT INTO \"case1\"(\"id\", \"firstName\", \"lastName\", \"age\") VALUES (NULL, ?, ?, 25) -- PARAMETERS: [\"Timber\",\"Saw\"]\nquery: INSERT INTO \"case1_audit\"(\"_seq\", \"_action\", \"_modifiedAt\", \"id\", \"firstName\", \"lastName\", \"age\") VALUES (NULL, ?, datetime('now'), 1, ?, ?, 25) -- PARAMETERS: [\"Create\",\"Timber\",\"Saw\"]\nquery: SELECT \"Case1Audit\".\"_seq\" AS \"Case1Audit__seq\", \"Case1Audit\".\"_modifiedAt\" AS \"Case1Audit__modifiedAt\" FROM \"case1_audit\" \"Case1Audit\" WHERE \"Case1Audit\".\"_seq\" = 1\nquery: COMMIT\nquery: SELECT \"Case1\".\"id\" AS \"Case1_id\", \"Case1\".\"firstName\" AS \"Case1_firstName\", \"Case1\".\"lastName\" AS \"Case1_lastName\", \"Case1\".\"age\" AS \"Case1_age\" FROM \"case1\" \"Case1\" WHERE \"Case1\".\"id\" IN (1)\n\nquery: BEGIN TRANSACTION\nquery: UPDATE \"case1\" SET \"age\" = 26 WHERE \"id\" IN (1)\nquery: INSERT INTO \"case1_audit\"(\"_seq\", \"_action\", \"_modifiedAt\", \"id\", \"firstName\", \"lastName\", \"age\") VALUES (NULL, ?, datetime('now'), 1, ?, ?, 26) -- PARAMETERS: [\"Update\",\"Timber\",\"Saw\"]\nquery: SELECT \"Case1Audit\".\"_seq\" AS \"Case1Audit__seq\", \"Case1Audit\".\"_modifiedAt\" AS \"Case1Audit__modifiedAt\" FROM \"case1_audit\" \"Case1Audit\" WHERE \"Case1Audit\".\"_seq\" = 2\nquery: COMMIT\nquery: SELECT \"Case1\".\"id\" AS \"Case1_id\", \"Case1\".\"firstName\" AS \"Case1_firstName\", \"Case1\".\"lastName\" AS \"Case1_lastName\", \"Case1\".\"age\" AS \"Case1_age\" FROM \"case1\" \"Case1\" WHERE \"Case1\".\"id\" IN (1)\n\nquery: BEGIN TRANSACTION\nquery: DELETE FROM \"case1\" WHERE \"id\" = 1\nquery: INSERT INTO \"case1_audit\"(\"_seq\", \"_action\", \"_modifiedAt\", \"id\", \"firstName\", \"lastName\", \"age\") VALUES (NULL, ?, datetime('now'), 1, ?, ?, 26) -- PARAMETERS: [\"Delete\",\"Timber\",\"Saw\"]\nquery: SELECT \"Case1Audit\".\"_seq\" AS \"Case1Audit__seq\", \"Case1Audit\".\"_modifiedAt\" AS \"Case1Audit__modifiedAt\" FROM \"case1_audit\" \"Case1Audit\" WHERE \"Case1Audit\".\"_seq\" = 3\nquery: COMMIT\n\nquery: SELECT \"Case1Audit\".\"_seq\" AS \"Case1Audit__seq\", \"Case1Audit\".\"_action\" AS \"Case1Audit__action\", \"Case1Audit\".\"_modifiedAt\" AS \"Case1Audit__modifiedAt\", \"Case1Audit\".\"id\" AS \"Case1Audit_id\", \"Case1Audit\".\"firstName\" AS \"Case1Audit_firstName\", \"Case1Audit\".\"lastName\" AS \"Case1Audit_lastName\", \"Case1Audit\".\"age\" AS \"Case1Audit_age\" FROM \"case1_audit\" \"Case1Audit\"\n[\n  Case1Audit {\n    _seq: 1,\n    _action: 'Create',\n    _modifiedAt: 2022-07-21T19:18:42.000Z,\n    id: 1,\n    firstName: 'Timber',\n    lastName: 'Saw',\n    age: 25\n  },\n  Case1Audit {\n    _seq: 2,\n    _action: 'Update',\n    _modifiedAt: 2022-07-21T19:18:42.000Z,\n    id: 1,\n    firstName: 'Timber',\n    lastName: 'Saw',\n    age: 26\n  },\n  Case1Audit {\n    _seq: 3,\n    _action: 'Delete',\n    _modifiedAt: 2022-07-21T19:18:42.000Z,\n    id: 1,\n    firstName: 'Timber',\n    lastName: 'Saw',\n    age: 26\n  }\n]\n\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkibae%2Ftypeorm-auditing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkibae%2Ftypeorm-auditing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkibae%2Ftypeorm-auditing/lists"}