TypeORM Entities

Basic Entity Definition

import {
  Entity, PrimaryGeneratedColumn, Column,
  CreateDateColumn, UpdateDateColumn, Index
} from "typeorm";

@Entity("users")
@Index(["email"])
@Index(["lastName", "firstName"])
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @PrimaryGeneratedColumn("uuid")
  uuid: string;

  @Column({ unique: true, length: 255 })
  email: string;

  @Column({ name: "first_name", length: 100 })
  firstName: string;

  @Column({ nullable: true })
  lastName?: string;

  @Column({ type: "enum", enum: ["admin","user","guest"], default: "user" })
  role: string;

  @Column({ type: "decimal", precision: 10, scale: 2, default: 0 })
  balance: number;

  @Column({ type: "jsonb", nullable: true })
  metadata: Record<string, unknown> | null;

  @Column({ default: true })
  isActive: boolean;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}

Relations

import { OneToMany, ManyToOne, ManyToMany, JoinTable, JoinColumn } from "typeorm";

// One-to-Many / Many-to-One
@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => User, user => user.posts, { onDelete: "CASCADE" })
  @JoinColumn({ name: "author_id" })
  author: User;

  @Column()
  authorId: number;
}

@Entity()
export class User {
  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}

// Many-to-Many with join table
@Entity()
export class Article {
  @ManyToMany(() => Tag, tag => tag.articles)
  @JoinTable({
    name: "article_tags",
    joinColumn:        { name: "article_id" },
    inverseJoinColumn: { name: "tag_id" }
  })
  tags: Tag[];
}

@Entity()
export class Tag {
  @ManyToMany(() => Article, article => article.tags)
  articles: Article[];
}

// One-to-One
@Entity()
export class Profile {
  @OneToOne(() => User, { onDelete: "CASCADE" })
  @JoinColumn()
  user: User;
}

Column Types & Options

OptionDescription
typeDB type: "varchar", "int", "text", "jsonb", "timestamp", "enum"
lengthColumn length for varchar
nullableAllow NULL (default: false)
uniqueUnique constraint
defaultDefault value
selectfalse = exclude from SELECT by default
nameOverride column name in DB
transformerCustom serialize/deserialize

Repository & QueryBuilder

import { AppDataSource } from "./data-source";

const userRepo = AppDataSource.getRepository(User);

// Basic CRUD
const user = await userRepo.findOne({ where: { email: "alice@example.com" } });
const users = await userRepo.find({
  where: { isActive: true },
  relations: ["posts"],
  order: { createdAt: "DESC" },
  take: 20, skip: 0
});
await userRepo.save({ email: "bob@example.com", role: "user" });
await userRepo.update({ id: 1 }, { isActive: false });
await userRepo.delete({ id: 1 });

// QueryBuilder for complex queries
const result = await userRepo.createQueryBuilder("u")
  .leftJoinAndSelect("u.posts", "p", "p.published = :pub", { pub: true })
  .where("u.role = :role", { role: "admin" })
  .andWhere("u.createdAt > :date", { date: new Date("2024-01-01") })
  .orderBy("u.createdAt", "DESC")
  .take(10)
  .getMany();

Listeners & Subscribers

import { BeforeInsert, BeforeUpdate, AfterLoad } from "typeorm";
import * as bcrypt from "bcrypt";

@Entity()
export class User {
  @Column({ select: false })
  password: string;

  @BeforeInsert()
  @BeforeUpdate()
  async hashPassword() {
    if (this.password) {
      this.password = await bcrypt.hash(this.password, 12);
    }
  }

  @AfterLoad()
  setFullName() {
    this.fullName = `${this.firstName} ${this.lastName}`;
  }
}