Skip to content

TypeORM

Databases & CachingDatabase/ORMTypeScript

What it is

TypeORM is a decorator-based ORM for TypeScript supporting both Active Record and Data Mapper patterns, with migrations, relations and a query builder.

Entities are classes with column decorators. Repositories provide CRUD; the query builder covers anything more complex.

Installation

npm install typeorm reflect-metadata pg

Getting started

The smallest useful thing you can do with it, and what each part means.

Entity and repository
@Entity()
export class Book {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 200 })
  title: string;

  @ManyToOne(() => Author, (author) => author.books)
  author: Author;

  @CreateDateColumn()
  createdAt: Date;
}

const repo = dataSource.getRepository(Book);

const books = await repo.find({
  where: { title: ILike('%dune%') },
  relations: { author: true },   // otherwise author is undefined
  order: { createdAt: 'DESC' },
  take: 10,
});
Relations are not loaded unless requested. Accessing book.author without specifying it gives undefined rather than an error, which makes the bug easy to miss.

Advanced usage

Where the library earns its place over a simpler alternative.

Query builder and transactions
const rows = await repo
  .createQueryBuilder('book')
  .innerJoinAndSelect('book.author', 'author')
  .where('book.year >= :year', { year: 1990 })   // always parameterise
  .andWhere('author.country = :country', { country: 'US' })
  .orderBy('book.year', 'DESC')
  .limit(10)
  .getMany();

await dataSource.transaction(async (manager) => {
  await manager.decrement(Stock, { sku }, 'quantity', qty);
  await manager.save(Order, { sku, qty });
});
Named parameters are mandatory — interpolating values into the where string is a SQL injection hole. decrement keeps the update atomic.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

No metadata for Book was found
The entity is not registered in DataSource entities, or reflect-metadata was not imported at the entry point.
Relation is undefined
It was not eagerly loaded. Add it to relations or join it in the query builder.

Best practices

  • Always specify relations explicitly; unloaded ones are silently undefined.
  • Use parameterised where clauses, never template interpolation.
  • Disable synchronize in production — it can drop columns. Use migrations.
  • Consider Prisma or Drizzle for new projects; their type inference is considerably stronger.

Background

Why it exists, and what it was reacting to.

TypeORM was the first widely adopted TypeScript ORM and remains the default in many NestJS projects, though its type inference is weaker than newer alternatives such as Prisma and Drizzle.