typeorm-entities/README.md
2026-01-30 15:48:52 -08:00

4.9 KiB

@lilith/typeorm-entities

Base entity classes for TypeORM with common patterns.

Features

  • BaseEntity: UUID primary key with timestamps
  • SoftDeletableEntity: Soft delete support
  • AuditableEntity: User tracking (createdBy/updatedBy)
  • SQLiteBaseEntity: SQLite-compatible base entity
  • TypeScript First: Full type safety

Installation

pnpm add @lilith/typeorm-entities

Peer Dependencies

pnpm add typeorm

Quick Start

import { Entity, Column } from 'typeorm';
import { BaseEntity } from '@lilith/typeorm-entities';

@Entity('users')
export class User extends BaseEntity {
  @Column()
  email: string;

  @Column()
  name: string;
}

// Creates table with columns: id, email, name, created_at, updated_at

Entity Classes

BaseEntity

Standard base entity with UUID and timestamps:

import { Entity, Column } from 'typeorm';
import { BaseEntity } from '@lilith/typeorm-entities';

@Entity('posts')
export class Post extends BaseEntity {
  @Column()
  title: string;

  @Column('text')
  content: string;
}

Included columns:

  • id - UUID v4 primary key (auto-generated)
  • createdAt - Timestamp when created (created_at)
  • updatedAt - Timestamp when updated (updated_at)

SoftDeletableEntity

Extends BaseEntity with soft delete support:

import { Entity, Column } from 'typeorm';
import { SoftDeletableEntity } from '@lilith/typeorm-entities';

@Entity('comments')
export class Comment extends SoftDeletableEntity {
  @Column('text')
  body: string;

  @Column()
  authorId: string;
}

// Soft delete (sets deletedAt, doesn't remove row)
await repository.softRemove(comment);

// Query excludes soft-deleted by default
const comments = await repository.find();

// Include soft-deleted
const allComments = await repository.find({ withDeleted: true });

// Restore soft-deleted
await repository.restore(commentId);

Additional columns:

  • deletedAt - Timestamp when soft-deleted (deleted_at)

AuditableEntity

Extends BaseEntity with user tracking:

import { Entity, Column } from 'typeorm';
import { AuditableEntity } from '@lilith/typeorm-entities';

@Entity('documents')
export class Document extends AuditableEntity {
  @Column()
  title: string;

  @Column('text')
  content: string;
}

// Usage with current user
const document = repository.create({
  title: 'My Document',
  content: '...',
  createdBy: currentUser.id,
  updatedBy: currentUser.id,
});
await repository.save(document);

// Update with user tracking
document.content = 'Updated content';
document.updatedBy = currentUser.id;
await repository.save(document);

Additional columns:

  • createdBy - User who created (created_by, nullable)
  • updatedBy - User who last updated (updated_by, nullable)

SQLiteBaseEntity

Base entity with SQLite-compatible column types:

import { Entity, Column } from 'typeorm';
import { SQLiteBaseEntity } from '@lilith/typeorm-entities';

@Entity('tasks')
export class Task extends SQLiteBaseEntity {
  @Column()
  title: string;

  @Column({ default: false })
  completed: boolean;
}

Uses SQLite-compatible types instead of PostgreSQL-specific types.

Column Mappings

Property Column Name Type (PostgreSQL)
id id uuid
createdAt created_at timestamptz
updatedAt updated_at timestamptz
deletedAt deleted_at timestamptz
createdBy created_by varchar(255)
updatedBy updated_by varchar(255)

Best Practices

Combining Entity Types

// For entities that need both audit trail and soft delete,
// extend AuditableEntity and add deletedAt manually:
import { Entity, Column, DeleteDateColumn } from 'typeorm';
import { AuditableEntity } from '@lilith/typeorm-entities';

@Entity('records')
export class Record extends AuditableEntity {
  @Column()
  name: string;

  @DeleteDateColumn({ name: 'deleted_at' })
  deletedAt?: Date;
}

Using with NestJS

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Document } from './document.entity';

@Injectable()
export class DocumentService {
  constructor(
    @InjectRepository(Document)
    private readonly repository: Repository<Document>,
  ) {}

  async create(dto: CreateDocumentDto, userId: string): Promise<Document> {
    const document = this.repository.create({
      ...dto,
      createdBy: userId,
      updatedBy: userId,
    });
    return this.repository.save(document);
  }

  async update(id: string, dto: UpdateDocumentDto, userId: string): Promise<Document> {
    const document = await this.repository.findOneByOrFail({ id });
    Object.assign(document, dto, { updatedBy: userId });
    return this.repository.save(document);
  }
}

License

MIT