CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typeorm

Data-Mapper ORM for TypeScript and ES2021+ supporting MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.

Overview
Eval results
Files

relationships.mddocs/

Relationship Management

Powerful relationship definition system supporting one-to-one, one-to-many, many-to-one, and many-to-many associations with advanced configuration options for modeling complex data relationships between entities.

Capabilities

Relationship Decorators

Core decorators for defining associations between entities with automatic foreign key management and lazy/eager loading support.

/**
 * Defines a one-to-one relationship between entities
 * @param typeFunctionOrTarget - Target entity class or string name
 * @param inverseSide - Property name on the target entity (optional)
 * @param options - Relationship configuration options
 */
function OneToOne<T>(
  typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
  inverseSide?: string | ((object: T) => any),
  options?: RelationOptions
): PropertyDecorator;

/**
 * Defines a one-to-many relationship (parent side of the relationship)
 * @param typeFunctionOrTarget - Target entity class or string name
 * @param inverseSide - Property name on the target entity that points back
 * @param options - Relationship configuration options
 */
function OneToMany<T>(
  typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
  inverseSide: string | ((object: T) => any),
  options?: RelationOptions
): PropertyDecorator;

/**
 * Defines a many-to-one relationship (child side of the relationship)
 * @param typeFunctionOrTarget - Target entity class or string name
 * @param inverseSide - Property name on the target entity (optional)
 * @param options - Relationship configuration options
 */
function ManyToOne<T>(
  typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
  inverseSide?: string | ((object: T) => any),
  options?: RelationOptions
): PropertyDecorator;

/**
 * Defines a many-to-many relationship between entities
 * @param typeFunctionOrTarget - Target entity class or string name
 * @param inverseSide - Property name on the target entity (optional)
 * @param options - Relationship configuration options
 */
function ManyToMany<T>(
  typeFunctionOrTarget: string | ((type?: any) => ObjectType<T>),
  inverseSide?: string | ((object: T) => any),
  options?: RelationOptions
): PropertyDecorator;

interface RelationOptions {
  /** Whether to load relation eagerly */
  eager?: boolean;
  /** Whether this relation is cascading */
  cascade?: boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];
  /** Custom foreign key column name */
  foreignKeyConstraintName?: string;
  /** Whether to create foreign key constraint */
  createForeignKeyConstraints?: boolean;
  /** Relation persistence/update options */
  persistence?: boolean;
  /** Whether relation can be null */
  nullable?: boolean;
  /** Default ordering for relations */
  orderBy?: Record<string, "ASC" | "DESC">;
  /** Relation comment */
  comment?: string;
}

Usage Examples:

import { Entity, Column, PrimaryGeneratedColumn, OneToOne, OneToMany, ManyToOne, ManyToMany } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  // One-to-One: User has one Profile
  @OneToOne(() => Profile, profile => profile.user, {
    cascade: true,
    eager: true
  })
  profile: Profile;

  // One-to-Many: User has many Posts
  @OneToMany(() => Post, post => post.author, {
    cascade: ["insert", "update"],
    orderBy: { createdAt: "DESC" }
  })
  posts: Post[];

  // Many-to-Many: User belongs to many Categories
  @ManyToMany(() => Category, category => category.users)
  categories: Category[];
}

@Entity()
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  bio: string;

  // Inverse side of One-to-One
  @OneToOne(() => User, user => user.profile)
  user: User;
}

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  // Many-to-One: Many Posts belong to one User
  @ManyToOne(() => User, user => user.posts, {
    nullable: false,
    onDelete: "CASCADE"
  })
  author: User;

  // Many-to-Many: Post belongs to many Tags
  @ManyToMany(() => Tag, tag => tag.posts, {
    cascade: true
  })
  tags: Tag[];
}

Join Configuration

Decorators for configuring how relationships are stored and queried in the database.

/**
 * Configures the join column for relationships
 * @param options - Join column configuration options
 */
function JoinColumn(options?: JoinColumnOptions | JoinColumnOptions[]): PropertyDecorator;

/**
 * Configures the join table for many-to-many relationships
 * @param options - Join table configuration options
 */
function JoinTable(options?: JoinTableOptions): PropertyDecorator;

interface JoinColumnOptions {
  /** Name of the foreign key column */
  name?: string;
  /** Name of the referenced column */
  referencedColumnName?: string;
  /** Whether foreign key column allows NULL */
  foreignKeyConstraintName?: string;
}

interface JoinTableOptions {
  /** Name of the join table */
  name?: string;
  /** Join column for the owning side */
  joinColumn?: JoinColumnOptions;
  /** Join column for the inverse side */
  inverseJoinColumn?: JoinColumnOptions;
  /** Database where join table is created */
  database?: string;
  /** Schema where join table is created */
  schema?: string;
}

Usage Examples:

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

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  // One-to-One with custom join column
  @OneToOne(() => Profile)
  @JoinColumn({
    name: "profile_id",
    referencedColumnName: "id",
    foreignKeyConstraintName: "FK_user_profile"
  })
  profile: Profile;

  // Many-to-Many with custom join table
  @ManyToMany(() => Role)
  @JoinTable({
    name: "user_roles",
    joinColumn: { name: "user_id", referencedColumnName: "id" },
    inverseJoinColumn: { name: "role_id", referencedColumnName: "id" }
  })
  roles: Role[];
}

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  // Many-to-One with custom foreign key column name
  @ManyToOne(() => User)
  @JoinColumn({ name: "author_id" })
  author: User;

  @ManyToOne(() => Category)
  @JoinColumn([
    { name: "category_id", referencedColumnName: "id" },
    { name: "category_type", referencedColumnName: "type" }
  ])
  category: Category;
}

Relation Loading and IDs

Utilities for loading relationship data and accessing foreign key IDs without loading the entire related entity.

/**
 * Exposes the ID of a relation without loading the relation
 * @param relation - Name of the relation property
 * @param options - Relation ID options
 */
function RelationId<T>(
  relation: string | ((object: T) => any),
  alias?: string,
  queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>
): PropertyDecorator;

/**
 * Exposes the count of a relation without loading the relation
 * @param relation - Name of the relation property
 * @param alias - Alias for the count property
 * @param queryBuilderFactory - Optional query builder for filtering
 */
function RelationCount<T>(
  relation: string | ((object: T) => any),
  alias?: string,
  queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>
): PropertyDecorator;

Usage Examples:

import { Entity, OneToMany, ManyToOne, RelationId, RelationCount } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];

  // Get post count without loading posts
  @RelationCount((user: User) => user.posts)
  postCount: number;

  // Get published post count with query builder
  @RelationCount((user: User) => user.posts, "publishedPostCount", qb =>
    qb.andWhere("post.published = :published", { published: true })
  )
  publishedPostCount: number;
}

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToOne(() => User, user => user.posts)
  author: User;

  // Get author ID without loading author
  @RelationId((post: Post) => post.author)
  authorId: number;

  @ManyToOne(() => Category)
  category: Category;

  @RelationId((post: Post) => post.category)
  categoryId: number;
}

// Usage in queries
const posts = await postRepository.find({
  select: ["id", "title", "authorId", "categoryId", "publishedAt"]
  // No need to join or load author/category relations
});

Cascade Operations

Configure automatic operations on related entities when parent entities are modified.

type CascadeOption = boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];

// Cascade examples in relation options
interface RelationOptions {
  cascade?: CascadeOption;
}

Cascade Behavior Examples:

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

  @OneToMany(() => Comment, comment => comment.author, {
    cascade: ["insert", "update"] // Only insert and update cascade
  })
  comments: Comment[];

  @OneToOne(() => Profile, profile => profile.user, {
    cascade: ["insert", "remove"] // Insert and remove cascade
  })
  profile: Profile;
}

// Usage - cascading will automatically save/remove related entities
const user = new User();
user.name = "John";
user.posts = [
  { title: "Post 1", content: "Content 1" },
  { title: "Post 2", content: "Content 2" }
];

// Saves user AND posts due to cascade
await userRepository.save(user);

// Removes user AND posts due to cascade
await userRepository.remove(user);

Tree Relationships

Special decorators for hierarchical/tree-like data structures.

/**
 * Marks entity as a tree structure
 * @param type - Tree type: "adjacency-list", "closure-table", "materialized-path"
 */
function Tree(type: "adjacency-list" | "closure-table" | "materialized-path"): ClassDecorator;

/**
 * Defines the parent relation in a tree structure
 */
function TreeParent(): PropertyDecorator;

/**
 * Defines the children relation in a tree structure
 */
function TreeChildren(): PropertyDecorator;

/**
 * Defines a column that stores the level/depth in the tree
 */
function TreeLevelColumn(): PropertyDecorator;

Tree Structure Examples:

import { Entity, Tree, TreeParent, TreeChildren, TreeLevelColumn } from "typeorm";

@Entity()
@Tree("adjacency-list")
export class Category {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @TreeParent()
  parent: Category;

  @TreeChildren()
  children: Category[];

  @TreeLevelColumn()
  level: number;
}

// Usage with TreeRepository
const treeRepository = dataSource.getTreeRepository(Category);

// Find all root categories
const roots = await treeRepository.findRoots();

// Find all descendants of a category
const descendants = await treeRepository.findDescendants(category);

// Find category ancestors
const ancestors = await treeRepository.findAncestors(category);

// Get the complete tree
const tree = await treeRepository.findTrees();

Relationship Patterns

Loading Strategies

  • Lazy Loading (default): Relations are loaded on-demand when accessed
  • Eager Loading: Relations are automatically loaded with the entity
  • Explicit Loading: Relations are loaded using repository methods or query builder

Foreign Key Constraints

TypeORM automatically creates foreign key constraints based on relationship definitions. You can customize constraint names and behavior:

@ManyToOne(() => User, {
  nullable: false,
  onDelete: "CASCADE", // Database-level cascade
  onUpdate: "CASCADE"
})
author: User;

Bidirectional Relationships

Always define both sides of bidirectional relationships for consistency:

// Parent side
@OneToMany(() => Post, post => post.author)
posts: Post[];

// Child side
@ManyToOne(() => User, user => user.posts)
author: User;

Install with Tessl CLI

npx tessl i tessl/npm-typeorm

docs

data-source.md

entity-definition.md

entity-schema.md

events.md

find-options.md

index.md

migrations.md

query-builder.md

relationships.md

repository.md

tile.json