Data-Mapper ORM for TypeScript and ES2021+ supporting MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.
Type-safe repository pattern implementation providing CRUD operations, custom queries, and specialized repositories for different data access patterns. TypeORM repositories abstract database operations with a clean, consistent API across all supported databases.
The primary repository class providing standard CRUD operations with full type safety and relation loading capabilities.
/**
* Repository for entity operations and queries
* @template Entity - The entity type this repository manages
*/
class Repository<Entity> {
/** The target entity class or schema */
readonly target: EntityTarget<Entity>;
/** The underlying entity manager */
readonly manager: EntityManager;
/** Create a new query builder for this entity */
createQueryBuilder(alias?: string): SelectQueryBuilder<Entity>;
/**
* Saves a given entity or array of entities
* @param entity - Entity or entities to save
* @param options - Save operation options
* @returns Promise resolving to saved entities
*/
save<T extends DeepPartial<Entity>>(
entity: T,
options?: SaveOptions
): Promise<T & Entity>;
save<T extends DeepPartial<Entity>>(
entities: T[],
options?: SaveOptions
): Promise<(T & Entity)[]>;
/**
* Removes a given entity or array of entities
* @param entity - Entity or entities to remove
* @param options - Remove operation options
* @returns Promise resolving to removed entities
*/
remove(entity: Entity, options?: RemoveOptions): Promise<Entity>;
remove(entities: Entity[], options?: RemoveOptions): Promise<Entity[]>;
/**
* Soft removes entities by setting their @DeleteDateColumn
* @param entity - Entity or entities to soft remove
* @returns Promise resolving to soft removed entities
*/
softRemove(entity: Entity, options?: SaveOptions): Promise<Entity>;
softRemove(entities: Entity[], options?: SaveOptions): Promise<Entity[]>;
/**
* Recovers soft-deleted entities by clearing @DeleteDateColumn
* @param entity - Entity or entities to recover
* @returns Promise resolving to recovered entities
*/
recover(entity: Entity, options?: SaveOptions): Promise<Entity>;
recover(entities: Entity[], options?: SaveOptions): Promise<Entity[]>;
/**
* Inserts new entities into database
* @param entity - Entity or entities to insert
* @returns Promise resolving to insert result
*/
insert(entity: QueryDeepPartialEntity<Entity> | QueryDeepPartialEntity<Entity>[]): Promise<InsertResult>;
/**
* Updates entities that match given conditions
* @param criteria - Update conditions
* @param partialEntity - Values to update
* @returns Promise resolving to update result
*/
update(
criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindOptionsWhere<Entity>,
partialEntity: QueryDeepPartialEntity<Entity>
): Promise<UpdateResult>;
/**
* Upsert operation (insert or update if exists)
* @param entityOrEntities - Entity or entities to upsert
* @param conflictPathsOrOptions - Conflict resolution paths or options
* @returns Promise resolving to upsert result
*/
upsert(
entityOrEntities: QueryDeepPartialEntity<Entity> | QueryDeepPartialEntity<Entity>[],
conflictPathsOrOptions: string[] | UpsertOptions<Entity>
): Promise<InsertResult>;
/**
* Deletes entities that match given conditions
* @param criteria - Delete conditions
* @returns Promise resolving to delete result
*/
delete(
criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindOptionsWhere<Entity>
): Promise<DeleteResult>;
/**
* Soft deletes entities that match given conditions
* @param criteria - Soft delete conditions
* @returns Promise resolving to update result (sets delete date)
*/
softDelete(
criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindOptionsWhere<Entity>
): Promise<UpdateResult>;
/**
* Restores soft-deleted entities that match given conditions
* @param criteria - Restore conditions
* @returns Promise resolving to update result (clears delete date)
*/
restore(
criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindOptionsWhere<Entity>
): Promise<UpdateResult>;
/**
* Finds entities that match given find options
* @param options - Find options
* @returns Promise resolving to entities array
*/
find(options?: FindManyOptions<Entity>): Promise<Entity[]>;
/**
* Finds entities that match given conditions
* @param where - Where conditions
* @returns Promise resolving to entities array
*/
findBy(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<Entity[]>;
/**
* Finds first entity that matches given find options
* @param options - Find options
* @returns Promise resolving to entity or null
*/
findOne(options: FindOneOptions<Entity>): Promise<Entity | null>;
/**
* Finds first entity that matches given conditions
* @param where - Where conditions
* @returns Promise resolving to entity or null
*/
findOneBy(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<Entity | null>;
/**
* Finds entity by ID, throws error if not found
* @param options - Find options
* @returns Promise resolving to entity
* @throws EntityNotFoundError if entity not found
*/
findOneOrFail(options: FindOneOptions<Entity>): Promise<Entity>;
/**
* Finds entity by conditions, throws error if not found
* @param where - Where conditions
* @returns Promise resolving to entity
* @throws EntityNotFoundError if entity not found
*/
findOneByOrFail(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<Entity>;
/**
* Counts entities that match given find options
* @param options - Count options
* @returns Promise resolving to count number
*/
count(options?: FindManyOptions<Entity>): Promise<number>;
/**
* Counts entities that match given conditions
* @param where - Where conditions
* @returns Promise resolving to count number
*/
countBy(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<number>;
/**
* Checks if entity exists that matches given conditions
* @param options - Existence check options
* @returns Promise resolving to boolean
*/
exist(options?: FindManyOptions<Entity>): Promise<boolean>;
/**
* Checks if entity exists that matches given conditions
* @param where - Where conditions
* @returns Promise resolving to boolean
*/
existsBy(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<boolean>;
/**
* Increments some column by provided value
* @param conditions - Update conditions
* @param propertyPath - Property path to increment
* @param value - Value to increment by
* @returns Promise resolving to update result
*/
increment(
conditions: FindOptionsWhere<Entity>,
propertyPath: string,
value: number | string
): Promise<UpdateResult>;
/**
* Decrements some column by provided value
* @param conditions - Update conditions
* @param propertyPath - Property path to decrement
* @param value - Value to decrement by
* @returns Promise resolving to update result
*/
decrement(
conditions: FindOptionsWhere<Entity>,
propertyPath: string,
value: number | string
): Promise<UpdateResult>;
/**
* Gets sum of values in numeric column
* @param columnName - Column name to sum
* @param where - Where conditions
* @returns Promise resolving to sum or null
*/
sum(
columnName: string,
where?: FindOptionsWhere<Entity>
): Promise<number | null>;
/**
* Gets average of values in numeric column
* @param columnName - Column name to average
* @param where - Where conditions
* @returns Promise resolving to average or null
*/
average(
columnName: string,
where?: FindOptionsWhere<Entity>
): Promise<number | null>;
/**
* Gets minimum value in column
* @param columnName - Column name
* @param where - Where conditions
* @returns Promise resolving to minimum value or null
*/
minimum(
columnName: string,
where?: FindOptionsWhere<Entity>
): Promise<any>;
/**
* Gets maximum value in column
* @param columnName - Column name
* @param where - Where conditions
* @returns Promise resolving to maximum value or null
*/
maximum(
columnName: string,
where?: FindOptionsWhere<Entity>
): Promise<any>;
/**
* Checks if entity has an ID value
* @param entity - Entity to check
* @returns True if entity has ID
*/
hasId(entity: Entity): boolean;
/**
* Gets entity ID value
* @param entity - Entity to get ID from
* @returns Entity ID value
*/
getId(entity: Entity): any;
/**
* Creates a new empty entity instance
* @returns New empty entity
*/
create(): Entity;
/**
* Creates entity from partial data
* @param entityLike - Partial entity data
* @returns New entity with provided data
*/
create(entityLike: DeepPartial<Entity>): Entity;
/**
* Creates multiple entities from array of partial data
* @param entityLikeArray - Array of partial entity data
* @returns Array of new entities
*/
create(entityLikeArray: DeepPartial<Entity>[]): Entity[];
/**
* Merges multiple entity data into a single entity
* @param mergeIntoEntity - Base entity to merge into
* @param entityLikes - Additional entity data to merge
* @returns Merged entity
*/
merge(mergeIntoEntity: Entity, ...entityLikes: DeepPartial<Entity>[]): Entity;
/**
* Preloads entity by finding it and loading all defined relations
* @param entityLike - Entity-like object to preload
* @returns Promise resolving to preloaded entity or undefined
*/
preload(entityLike: DeepPartial<Entity>): Promise<Entity | undefined>;
/**
* Clears all entities from the table (truncate)
* @returns Promise resolving when clear is complete
*/
clear(): Promise<void>;
/**
* Checks if any entity exists matching given options
* @param options - Find options to check existence
* @returns Promise resolving to true if any entity exists
*/
exist(options?: FindManyOptions<Entity>): Promise<boolean>;
/**
* Checks if any entity exists matching given where conditions
* @param where - Where conditions to check
* @returns Promise resolving to true if any entity exists
*/
existBy(where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[]): Promise<boolean>;
}
interface SaveOptions {
/** Array of listeners to skip */
listeners?: boolean;
/** Whether to skip transaction for this operation */
transaction?: boolean;
/** Chunk size for bulk operations */
chunk?: number;
/** Whether to reload entity after save */
reload?: boolean;
}
interface RemoveOptions {
/** Array of listeners to skip */
listeners?: boolean;
/** Whether to skip transaction for this operation */
transaction?: boolean;
/** Chunk size for bulk operations */
chunk?: number;
}
interface UpsertOptions<Entity> {
/** Columns that define uniqueness constraint */
conflictPaths: string[];
/** Whether to skip update on conflict */
skipUpdateIfNoValuesChanged?: boolean;
/** Index predicate for partial unique indexes */
indexPredicate?: string;
}Usage Examples:
import { Repository } from "typeorm";
import { User } from "./entities/User";
// Get repository from data source
const userRepository = dataSource.getRepository(User);
// Basic CRUD operations
const user = await userRepository.save({
name: "John Doe",
email: "john@example.com"
});
const users = await userRepository.find();
const userById = await userRepository.findOneBy({ id: 1 });
await userRepository.update({ id: 1 }, { name: "Jane Doe" });
await userRepository.delete({ id: 1 });
// Soft delete operations
await userRepository.softDelete({ id: 1 });
await userRepository.restore({ id: 1 });
// Aggregation operations
const userCount = await userRepository.count();
const totalAge = await userRepository.sum("age");
const averageAge = await userRepository.average("age");
// Bulk operations
await userRepository.insert([
{ name: "User 1", email: "user1@example.com" },
{ name: "User 2", email: "user2@example.com" }
]);
// Upsert operation
await userRepository.upsert(
{ email: "john@example.com", name: "John Smith" },
{ conflictPaths: ["email"] }
);Specialized repository for hierarchical data structures with tree-specific operations.
/**
* Repository for tree entities with hierarchical operations
* @template Entity - Tree entity type
*/
class TreeRepository<Entity> extends Repository<Entity> {
/**
* Finds all root nodes (nodes without parents)
* @param options - Find options
* @returns Promise resolving to root nodes
*/
findRoots(options?: FindTreeOptions): Promise<Entity[]>;
/**
* Finds all descendants of a given node
* @param entity - Parent entity
* @param options - Find options
* @returns Promise resolving to descendant nodes
*/
findDescendants(entity: Entity, options?: FindTreeOptions): Promise<Entity[]>;
/**
* Finds all ancestors of a given node
* @param entity - Child entity
* @param options - Find options
* @returns Promise resolving to ancestor nodes
*/
findAncestors(entity: Entity, options?: FindTreeOptions): Promise<Entity[]>;
/**
* Finds complete trees with all nodes
* @param options - Find options
* @returns Promise resolving to complete tree structures
*/
findTrees(options?: FindTreeOptions): Promise<Entity[]>;
/**
* Counts descendants of a given node
* @param entity - Parent entity
* @param options - Count options
* @returns Promise resolving to descendant count
*/
countDescendants(entity: Entity, options?: FindTreeOptions): Promise<number>;
/**
* Counts ancestors of a given node
* @param entity - Child entity
* @param options - Count options
* @returns Promise resolving to ancestor count
*/
countAncestors(entity: Entity, options?: FindTreeOptions): Promise<number>;
/**
* Creates query builder to find descendants
* @param alias - Alias for the entity
* @param entity - Parent entity
* @returns SelectQueryBuilder for descendants
*/
createDescendantsQueryBuilder(
alias: string,
entity: Entity
): SelectQueryBuilder<Entity>;
/**
* Creates query builder to find ancestors
* @param alias - Alias for the entity
* @param entity - Child entity
* @returns SelectQueryBuilder for ancestors
*/
createAncestorsQueryBuilder(
alias: string,
entity: Entity
): SelectQueryBuilder<Entity>;
}
interface FindTreeOptions {
/** Relations to load */
relations?: string[];
/** Tree depth limit */
depth?: number;
}Tree Repository Examples:
import { TreeRepository } from "typeorm";
import { Category } from "./entities/Category";
const categoryRepository = dataSource.getTreeRepository(Category);
// Find all root categories
const roots = await categoryRepository.findRoots();
// Find all subcategories under a category
const subcategories = await categoryRepository.findDescendants(parentCategory);
// Find category path (all ancestors)
const path = await categoryRepository.findAncestors(childCategory);
// Load complete category tree
const tree = await categoryRepository.findTrees();
// Count subcategories
const subcategoryCount = await categoryRepository.countDescendants(parentCategory);Specialized repository for MongoDB with document-specific operations and MongoDB query syntax.
/**
* Repository for MongoDB entities with document operations
* @template Entity - MongoDB entity type
*/
class MongoRepository<Entity> extends Repository<Entity> {
/**
* Creates a cursor for the entities
* @param query - MongoDB query
* @returns Cursor for iteration
*/
createCursor<T = any>(query?: ObjectLiteral): Cursor<T>;
/**
* Creates an aggregation cursor
* @param pipeline - Aggregation pipeline
* @returns Aggregation cursor
*/
aggregate<R = any>(pipeline: ObjectLiteral[]): AggregationCursor<R>;
/**
* Finds entities using MongoDB query syntax
* @param query - MongoDB query
* @param options - Find options
* @returns Promise resolving to entities
*/
findByIds(ids: any[], options?: FindManyOptions<Entity>): Promise<Entity[]>;
/**
* Finds one entity by MongoDB ObjectId
* @param id - MongoDB ObjectId
* @param options - Find options
* @returns Promise resolving to entity or null
*/
findOneById(id: string | ObjectID, options?: FindOneOptions<Entity>): Promise<Entity | null>;
/**
* Creates distinct query
* @param key - Field to get distinct values for
* @param query - MongoDB query
* @returns Promise resolving to distinct values
*/
distinct(key: string, query?: ObjectLiteral): Promise<any>;
/**
* Updates many documents
* @param query - MongoDB query
* @param update - Update operations
* @returns Promise resolving to update result
*/
updateMany(
query: ObjectLiteral,
update: ObjectLiteral
): Promise<UpdateResult>;
/**
* Deletes many documents
* @param query - MongoDB query
* @returns Promise resolving to delete result
*/
deleteMany(query: ObjectLiteral): Promise<DeleteResult>;
/**
* Finds and modifies a document atomically
* @param query - MongoDB query
* @param update - Update operations
* @param options - Find and modify options
* @returns Promise resolving to modified document
*/
findOneAndUpdate(
query: ObjectLiteral,
update: ObjectLiteral,
options?: { returnOriginal?: boolean; upsert?: boolean }
): Promise<Entity | null>;
/**
* Finds and deletes a document atomically
* @param query - MongoDB query
* @returns Promise resolving to deleted document
*/
findOneAndDelete(query: ObjectLiteral): Promise<Entity | null>;
}Direct database operations interface with transaction support, providing lower-level access to database operations.
/**
* Entity manager for direct database operations
*/
class EntityManager {
/** DataSource connection */
readonly connection: DataSource;
/** Query runner for this manager */
readonly queryRunner?: QueryRunner;
/**
* Gets repository for entity
* @param target - Entity target
* @returns Repository instance
*/
getRepository<Entity>(target: EntityTarget<Entity>): Repository<Entity>;
/**
* Gets tree repository for entity
* @param target - Entity target
* @returns TreeRepository instance
*/
getTreeRepository<Entity>(target: EntityTarget<Entity>): TreeRepository<Entity>;
/**
* Gets MongoDB repository for entity
* @param target - Entity target
* @returns MongoRepository instance
*/
getMongoRepository<Entity>(target: EntityTarget<Entity>): MongoRepository<Entity>;
/**
* Creates a new query builder
* @param entityClass - Entity class
* @param alias - Table alias
* @returns SelectQueryBuilder instance
*/
createQueryBuilder<Entity>(
entityClass?: ObjectType<Entity> | EntitySchema<Entity>,
alias?: string
): SelectQueryBuilder<Entity>;
/** All repository methods available on EntityManager */
save<T>(entity: T, options?: SaveOptions): Promise<T>;
remove<T>(entity: T, options?: RemoveOptions): Promise<T>;
insert<Entity>(target: EntityTarget<Entity>, entity: QueryDeepPartialEntity<Entity>): Promise<InsertResult>;
update<Entity>(target: EntityTarget<Entity>, criteria: any, partialEntity: QueryDeepPartialEntity<Entity>): Promise<UpdateResult>;
delete<Entity>(target: EntityTarget<Entity>, criteria: any): Promise<DeleteResult>;
find<Entity>(entityClass: EntityTarget<Entity>, options?: FindManyOptions<Entity>): Promise<Entity[]>;
findOne<Entity>(entityClass: EntityTarget<Entity>, options: FindOneOptions<Entity>): Promise<Entity | null>;
// ... all other repository methods
}Base classes for implementing custom repositories and Active Record pattern.
/**
* Abstract base class for custom repositories
* @template Entity - Entity type
*/
abstract class AbstractRepository<Entity> {
/** Repository for the entity */
readonly repository: Repository<Entity>;
/** Entity manager */
readonly manager: EntityManager;
/**
* Creates query builder for the entity
* @param alias - Table alias
* @returns SelectQueryBuilder instance
*/
createQueryBuilder(alias?: string): SelectQueryBuilder<Entity>;
}
/**
* Base entity class providing Active Record pattern methods
*/
abstract class BaseEntity {
/** Whether this entity has an ID assigned */
hasId(): boolean;
/** Saves this entity */
save(options?: SaveOptions): Promise<this>;
/** Removes this entity */
remove(options?: RemoveOptions): Promise<this>;
/** Soft removes this entity */
softRemove(options?: SaveOptions): Promise<this>;
/** Recovers this soft-removed entity */
recover(options?: SaveOptions): Promise<this>;
/** Reloads this entity from database */
reload(): Promise<void>;
/** Static methods for entity operations */
static save<T extends BaseEntity>(this: ObjectType<T>, entity: T, options?: SaveOptions): Promise<T>;
static save<T extends BaseEntity>(this: ObjectType<T>, entities: T[], options?: SaveOptions): Promise<T[]>;
static remove<T extends BaseEntity>(this: ObjectType<T>, entity: T, options?: RemoveOptions): Promise<T>;
static remove<T extends BaseEntity>(this: ObjectType<T>, entities: T[], options?: RemoveOptions): Promise<T[]>;
static insert<T extends BaseEntity>(this: ObjectType<T>, entity: QueryDeepPartialEntity<T>): Promise<InsertResult>;
static update<T extends BaseEntity>(this: ObjectType<T>, criteria: any, partialEntity: QueryDeepPartialEntity<T>): Promise<UpdateResult>;
static delete<T extends BaseEntity>(this: ObjectType<T>, criteria: any): Promise<DeleteResult>;
static find<T extends BaseEntity>(this: ObjectType<T>, options?: FindManyOptions<T>): Promise<T[]>;
static findOne<T extends BaseEntity>(this: ObjectType<T>, options: FindOneOptions<T>): Promise<T | null>;
static findOneBy<T extends BaseEntity>(this: ObjectType<T>, where: FindOptionsWhere<T>): Promise<T | null>;
// ... other static repository methods
}Active Record Pattern Example:
import { Entity, BaseEntity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
// Custom instance methods
async updateLastLogin(): Promise<void> {
this.lastLoginAt = new Date();
await this.save();
}
}
// Active Record usage
const user = new User();
user.name = "John";
user.email = "john@example.com";
await user.save(); // Saves the user
// Static methods
const users = await User.find();
const userById = await User.findOneBy({ id: 1 });
await User.delete({ id: 1 });Create custom repositories by extending Repository or using AbstractRepository:
@CustomRepository(User)
export class UserRepository extends Repository<User> {
findActiveUsers(): Promise<User[]> {
return this.findBy({ active: true });
}
findByEmail(email: string): Promise<User | null> {
return this.findOneBy({ email });
}
}
// Register custom repository
const userRepository = dataSource.getCustomRepository(UserRepository);TypeORM supports multiple repository patterns:
Install with Tessl CLI
npx tessl i tessl/npm-typeorm