@nestjs/typeorm, typeorm, and a database driver, then register TypeOrmModule.forRoot({ ... }) in your root module with the connection config (the old ormconfig.json is gone). Decorate classes with @Entity and @Column, wire relations, then inject repositories with @InjectRepository to run typed queries.TypeORM is one of the most widely used ORMs in the Node.js world, and NestJS ships first-class support for it through the @nestjs/typeorm package. Because both are TypeScript-first, they fit together cleanly: you model tables as decorated classes and let dependency injection hand you a repository for each one. This tutorial walks through a working setup from install to relations, using the patterns that are current in 2026 — not the deprecated ormconfig.json and findOne(id) conventions you will still find in older guides.
What is TypeORM, and why use it with NestJS?
An ORM (object-relational mapper) lets you work with database rows as regular objects instead of hand-writing SQL for every query. It automates the object-to-table and table-to-object conversion, which lowers development and maintenance cost.
TypeORM is an ORM that runs on Node.js (plus Browser, React Native, Electron, and more) and works with both TypeScript and modern JavaScript. It reached its long-awaited 1.0 release in May 2026, signalling renewed maintenance after years on the 0.3.x line. With NestJS you would typically pair the latest @nestjs/typeorm (v11+) with TypeORM 1.0.
Reasons teams reach for TypeORM in a NestJS app:
- It is TypeScript-native, so entities double as your type definitions.
- NestJS injects repositories for you — no manual connection plumbing in services.
- It supports both the Repository pattern and the ActiveRecord pattern.
- It ships migrations, query builder, eager/lazy relations, and transactions out of the box.
The main alternative is Prisma, which is schema-first and generates a typed client from a schema.prisma file. Prefer Prisma if you want strict, generated migrations; prefer TypeORM if you want your decorated classes to be the single source of truth.
How do I install TypeORM in a NestJS project?
Install the NestJS integration, TypeORM itself, and the driver for your database. For PostgreSQL:
npm install --save @nestjs/typeorm typeorm pgSwap the driver for your database — mysql2 for MySQL/MariaDB, better-sqlite3 for SQLite, mssql for SQL Server. That is the only package that changes; the rest of the code below is database-agnostic.
How do I connect TypeORM to the database (no ormconfig.json)?
Older tutorials tell you to create an ormconfig.json file and call TypeOrmModule.forRoot() with no arguments. That approach is deprecated. The modern way is to pass the connection config directly into forRoot inside your root module.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'library',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // dev only — never in production
}),
],
})
export class AppModule {}Two things to keep in mind:
synchronize: trueis for development only. It auto-creates and alters tables to match your entities, which is convenient locally but will silently drop columns and data in production. Use migrations in production instead.- The
entitiesglob must point at the compiled location.__dirname + '/**/*.entity{.ts,.js}'resolves correctly whether you run fromsrc(ts-node) ordist(built).
For anything beyond a demo, read secrets from config rather than hard-coding them. forRootAsync lets you inject ConfigService:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get('DB_HOST'),
port: config.get('DB_PORT'),
username: config.get('DB_USER'),
password: config.get('DB_PASS'),
database: config.get('DB_NAME'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
}),
});How do I create an entity (a table)?
In TypeORM a model equals a database table. You define one by decorating a class with @Entity() and each persisted field with @Column(). A class without @Entity() is just a plain class — no table is created for it.
// src/db/entity/cat.entity.ts
import { Entity, Column } from 'typeorm';
@Entity()
export class Cat {
@Column()
name: string;
@Column()
breed: string;
@Column()
age: number;
}Every table needs a primary key. Use @PrimaryGeneratedColumn() for an auto-incrementing id (or @PrimaryColumn() if you assign the key yourself):
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Cat {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 200 })
name: string;
@Column()
breed: string;
@Column()
age: number;
}How do relationships work in TypeORM?
A relationship exists when one table references another through a foreign key. TypeORM models the three classic relational shapes with decorators:
- One-to-One —
@OneToOne() - One-to-Many / Many-to-One —
@OneToMany()paired with@ManyToOne() - Many-to-Many —
@ManyToMany()with@JoinTable()on the owning side
The arrow functions you pass to these decorators (for example () => Book) defer resolving the related class, which avoids circular-import problems between two entity files that reference each other.
A full example: users, books, and genres
Let us build a small library API with three entities and two relationships:
- User → Book: one-to-many (a user owns many books).
- Book → Genre: many-to-many (a book can belong to several genres).
User entity — the one-to-many side:
// src/db/entity/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Book } from './book.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 500 })
name: string;
// 1:n — a user has many books
@OneToMany(() => Book, (book) => book.user)
books: Book[];
}Book entity — holds the many-to-one back-reference and owns the many-to-many join:
// src/db/entity/book.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
ManyToMany,
JoinTable,
} from 'typeorm';
import { User } from './user.entity';
import { Genre } from './genre.entity';
@Entity()
export class Book {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 500 })
name: string;
// n:1 — each book belongs to one user
@ManyToOne(() => User, (user) => user.books)
user: User;
// n:n — a book can have many genres
@ManyToMany(() => Genre)
@JoinTable()
genres: Genre[];
}Genre entity:
// src/db/entity/genre.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Genre {
@PrimaryGeneratedColumn()
id: number;
@Column()
type: string;
}The @JoinTable() on Book.genres tells TypeORM that Book is the owning side of the many-to-many, so the join table (book_genres_genre) is created and managed from there.
How do I inject and use a repository?
NestJS recommends the Repository pattern over ActiveRecord because it plays well with dependency injection and testing. First, register the entities a module needs with forFeature so the repositories become injectable inside that module:
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../db/entity/user.entity';
import { Book } from '../db/entity/book.entity';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
@Module({
imports: [TypeOrmModule.forFeature([User, Book])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}Then inject the repository with @InjectRepository and query it. Note the modern findOne signature — it takes a FindOptions object, not a bare id:
// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../db/entity/user.entity';
import { Book } from '../db/entity/book.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
) {}
create(name: string): Promise<User> {
const user = this.usersRepository.create({ name });
return this.usersRepository.save(user);
}
findAll(): Promise<User[]> {
return this.usersRepository.find();
}
async findBooks(userId: number): Promise<Book[]> {
const user = await this.usersRepository.findOne({
where: { id: userId },
relations: { books: true },
});
if (!user) throw new NotFoundException(`User ${userId} not found`);
return user.books;
}
}And a thin controller that maps HTTP routes onto those service methods:
// src/users/users.controller.ts
import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body('name') name: string) {
return this.usersService.create(name);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id/books')
findBooks(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findBooks(id);
}
}Finally, pull the feature module into your root module alongside TypeOrmModule.forRoot(...):
// src/app.module.ts (excerpt)
@Module({
imports: [
TypeOrmModule.forRoot({ /* ...connection config... */ }),
UsersModule,
],
})
export class AppModule {}What changed in TypeORM 0.3 and 1.0?
If you are porting an older tutorial, these are the breaking changes that trip people up most:
ormconfig.jsonis gone. Pass config toforRoot/DataSourcedirectly.findOne(id)and no-argfindOne()were removed. UsefindOneBy({ id })orfindOne({ where: { id } }).findByIds([...])is gone. UsefindBy({ id: In([...]) }).null/undefinedin awherenow throw instead of being silently ignored — useIsNull()to match NULLs on purpose.- The legacy
Connectionclass is replaced byDataSource.
How do I fix "EntityMetadataNotFoundError: No metadata for UserEntity was found"?
This is the most common first-run error with TypeORM and NestJS. It means TypeORM created its connection but never registered the entity you are trying to use. Work through these causes in order:
- The entity is not in the
entitiesarray (or glob). If you list entities explicitly inforRoot, make sure the class is in that list; if you use a glob, confirm the filename actually matches the pattern (for example*.entity.ts). - The glob points at the wrong folder. A path like
src/**/*.entity.tswill match nothing after the app is compiled todist. Use__dirname + '/**/*.entity{.ts,.js}'so it resolves in both dev and production. - The
@Entity()decorator is missing on the class, oremitDecoratorMetadata/experimentalDecoratorsare not enabled intsconfig.json. - The repository was not registered with
forFeature. Every module that injects a repository must importTypeOrmModule.forFeature([Entity]). - Duplicate TypeORM installs (two copies in
node_modules) can register metadata against a different instance. Deduplicate your lockfile.
Nine times out of ten it is the glob path — fix that first.
FAQ
Is TypeORM still maintained in 2026?
Yes. After years on the 0.3.x line, TypeORM shipped its 1.0 release in May 2026, and the NestJS integration (@nestjs/typeorm v11+) tracks it. It remains one of the most-used ORMs in the Node.js ecosystem.
Do I still need ormconfig.json?
No. The ormconfig.json file is deprecated. Pass your connection settings directly to TypeOrmModule.forRoot({ ... }), or use forRootAsync with ConfigService to read them from environment variables.
What is the difference between forRoot and forFeature?
forRoot is called once in the root module to establish the database connection. forFeature is called in each feature module to register the specific entities whose repositories that module needs to inject.
Should I use TypeORM or Prisma with NestJS?
Both are excellent. TypeORM keeps your decorated entity classes as the source of truth and supports the Repository and ActiveRecord patterns natively in NestJS. Prisma is schema-first with a generated, strongly typed client and very clean migrations. Choose TypeORM for decorator-driven modelling, Prisma for schema-driven migrations.
ActiveRecord or Repository pattern in NestJS?
Use the Repository pattern. Injecting Repository<Entity> with @InjectRepository keeps entities free of persistence logic and makes services far easier to unit-test with mocked repositories. ActiveRecord (extending BaseEntity) works but couples your models to the active connection.
Wrapping up
You now have a NestJS project wired to TypeORM the 2026 way: config passed straight into forRoot, entities defined with decorators, one-to-many and many-to-many relations in place, and repositories injected into services with the current findOne({ where }) API. From here, add migrations for production, layer in validation with DTOs and class-validator, and introduce transactions where you need atomic writes.
Building a backend on NestJS and TypeORM and need experienced hands? Codersera helps you hire vetted remote developers and extend your engineering team quickly, with lower hiring risk. You can also explore our NestJS, TypeORM and GraphQL DataLoader tutorial to take these concepts further.