Data-Mapper ORM for TypeScript and ES2021+ supporting MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.
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.
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[];
}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;
}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
});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);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();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;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