NestJS Repositories
A repository is the layer that communicates directly with the database. In TypeORM, every entity gets a repository automatically. The repository provides methods to find, save, update, and delete records without writing SQL. Your service injects the repository and calls its methods to perform all database operations.
Injecting a Repository
Use @InjectRepository(Entity) to inject a repository into a service:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
}
TypeORM provides the Repository<User> instance automatically. You call its methods inside your service methods.
Common Repository Methods
Finding Records
// Find all users
const users = await this.userRepository.find();
// Find with a condition
const admins = await this.userRepository.find({
where: { role: 'admin' }
});
// Find one by condition
const user = await this.userRepository.findOne({
where: { id: 1 }
});
// Find one or throw NotFoundException automatically
const user = await this.userRepository.findOneOrFail({
where: { id: 1 }
});
Creating Records
// Step 1: Create the entity instance
const newUser = this.userRepository.create({
name: 'Alice',
email: 'alice@example.com',
age: 30,
});
// Step 2: Save it to the database
const savedUser = await this.userRepository.save(newUser);
// savedUser now has the auto-generated id
Updating Records
// Approach 1: Save a modified entity
const user = await this.userRepository.findOne({ where: { id: 1 } });
user.name = 'Alice Updated';
await this.userRepository.save(user);
// Approach 2: Update by ID directly
await this.userRepository.update(1, { name: 'Alice Updated' });
Deleting Records
// Hard delete by ID
await this.userRepository.delete(1);
// Soft delete (sets deletedAt timestamp)
await this.userRepository.softDelete(1);
// Remove an entity instance
const user = await this.userRepository.findOne({ where: { id: 1 } });
await this.userRepository.remove(user);
A Complete Service Example
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
findAll(): Promise<User[]> {
return this.userRepository.find();
}
async findOne(id: number): Promise<User> {
const user = await this.userRepository.findOne({ where: { id } });
if (!user) throw new NotFoundException(`User #${id} not found`);
return user;
}
create(dto: CreateUserDto): Promise<User> {
const user = this.userRepository.create(dto);
return this.userRepository.save(user);
}
async update(id: number, dto: UpdateUserDto): Promise<User> {
const user = await this.findOne(id);
Object.assign(user, dto);
return this.userRepository.save(user);
}
async remove(id: number): Promise<void> {
const user = await this.findOne(id);
await this.userRepository.remove(user);
}
}
Repository Flow Diagram
Controller Service Repository Database
| | | |
GET /users/1 | | |
|──────────────→ | | |
| findOne(1) | |
| |──────────────────→ | |
| | findOne({id:1}) | |
| | |──────────────→ |
| | | SELECT * FROM |
| | | users WHERE id=1 |
| | | ←──────────────|
| | ←──────────────────| { id:1, ...} |
| ←──────────────| | |
response | | |
Advanced Finding with Relations
When an entity has relations (covered in the next topic), you load related data using the relations option:
const user = await this.userRepository.findOne({
where: { id: 1 },
relations: ['orders', 'orders.items'], // load user's orders and their items
});
The QueryBuilder
For complex queries that go beyond simple find options, TypeORM's QueryBuilder generates SQL programmatically:
const users = await this.userRepository
.createQueryBuilder('user')
.where('user.age > :minAge', { minAge: 18 })
.andWhere('user.role = :role', { role: 'admin' })
.orderBy('user.name', 'ASC')
.limit(10)
.getMany();
Custom Repositories
When your service needs database methods that the default repository does not provide, extend the repository in a custom class:
@Injectable()
export class UserCustomRepository {
constructor(
@InjectRepository(User)
private readonly repo: Repository<User>,
) {}
findActiveAdmins(): Promise<User[]> {
return this.repo.find({
where: { role: 'admin', isActive: true },
order: { createdAt: 'DESC' },
});
}
}
Custom repository methods keep complex query logic out of your service and give descriptive names to database operations that recur throughout your application.
