Skip to content

Crud_operations

Complete Guide to Create, Read, Update, and Delete Operations

Section titled “Complete Guide to Create, Read, Update, and Delete Operations”

CRUD operations are the fundamental operations for any database application.

CRUD Operations Flow
================================================================================
CRUD Operations
|
+-----------+-----------+-----------+-----------+
| | | | |
v v v v v
+-------+ +-------+ +-------+ +-------+ +-------+
| CREATE| | READ | | UPDATE| | DELETE| | COUNT |
+-------+ +-------+ +-------+ +-------+ +-------+
| | | | |
v v v v v
INSERT SELECT UPDATE DELETE COUNT
save() find() save() remove() count()
insert() findOne() update() delete()
create() findBy()
================================================================================

import { AppDataSource } from './data-source';
import { User } from './entities/user.entity';
const userRepository = AppDataSource.getRepository(User);
// Step 1: Create entity instance (not saved)
const user = userRepository.create({
name: 'John Doe',
email: 'john@example.com',
isActive: true,
});
// Step 2: Save to database
await userRepository.save(user);
console.log(user.id); // Auto-generated ID
// Create and save in one step
const user = new User();
user.name = 'John Doe';
user.email = 'john@example.com';
await userRepository.save(user);
// Insert is faster for new entities
// Returns insert result, not entity
const result = await userRepository.insert({
name: 'John Doe',
email: 'john@example.com',
});
console.log(result.identifiers); // [{ id: 1 }]
// Method 1: Using save() with array
const users = userRepository.create([
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' },
{ name: 'User 3', email: 'user3@example.com' },
]);
await userRepository.save(users);
// Method 2: Using insert() with array
await userRepository.insert([
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' },
{ name: 'User 3', email: 'user3@example.com' },
]);
// Create user with posts
const user = userRepository.create({
name: 'John Doe',
email: 'john@example.com',
posts: [
{ title: 'First Post', content: 'Content 1' },
{ title: 'Second Post', content: 'Content 2' },
],
});
await userRepository.save(user);
// Inserts user AND posts (if cascade is enabled)

// Get all users
const users = await userRepository.find();
// With conditions
const activeUsers = await userRepository.find({
where: { isActive: true },
});
// Find by ID
const user = await userRepository.findOne({
where: { id: 1 },
});
// Find by condition
const user = await userRepository.findOne({
where: { email: 'john@example.com' },
});
// Find or throw
const user = await userRepository.findOneOrFail({
where: { id: 1 },
});
// Throws EntityNotFoundError if not found
// Simple find by conditions
const users = await userRepository.findBy({
isActive: true,
role: 'admin',
});
// Load related entities
const user = await userRepository.findOne({
where: { id: 1 },
relations: ['posts', 'posts.comments', 'profile'],
});
console.log(user.posts[0].comments);
// Select specific columns
const users = await userRepository.find({
select: ['id', 'name', 'email'],
});
// Select with relations
const user = await userRepository.findOne({
where: { id: 1 },
select: {
id: true,
name: true,
posts: {
id: true,
title: true,
},
},
relations: ['posts'],
});
// Order by single column
const users = await userRepository.find({
order: { createdAt: 'DESC' },
});
// Order by multiple columns
const users = await userRepository.find({
order: {
role: 'ASC',
createdAt: 'DESC',
},
});
// Skip and take
const users = await userRepository.find({
skip: 0, // Offset
take: 10, // Limit
});
// With page number
const page = 2;
const limit = 10;
const users = await userRepository.find({
skip: (page - 1) * limit,
take: limit,
});
// Find and count (for pagination info)
const [users, total] = await userRepository.findAndCount({
skip: 0,
take: 10,
});
console.log(`Page 1 of ${Math.ceil(total / 10)}`);

// Find, modify, save
const user = await userRepository.findOne({ where: { id: 1 } });
user.name = 'Updated Name';
user.email = 'updated@example.com';
await userRepository.save(user);
// Update by condition
await userRepository.update(
{ id: 1 },
{ name: 'Updated Name' }
);
// Update multiple
await userRepository.update(
{ isActive: false },
{ isActive: true }
);
// Update by ID
await userRepository.update(1, { name: 'Updated Name' });
// Preload creates entity from existing data
const user = await userRepository.preload({
id: 1,
name: 'Updated Name',
email: 'updated@example.com',
});
if (user) {
await userRepository.save(user);
}
// Update only provided fields
const updateData: Partial<User> = {
name: 'New Name',
};
await userRepository.update({ id: 1 }, updateData);
// Only 'name' is updated, other fields remain unchanged
// Update user and add new post
const user = await userRepository.findOne({
where: { id: 1 },
relations: ['posts'],
});
user.posts.push({ title: 'New Post', content: 'Content' });
await userRepository.save(user);

// Remove entity instance
const user = await userRepository.findOne({ where: { id: 1 } });
await userRepository.remove(user);
// Remove multiple
const users = await userRepository.find({ where: { isActive: false } });
await userRepository.remove(users);
// Delete by ID
await userRepository.delete(1);
// Delete by condition
await userRepository.delete({ isActive: false });
// Delete multiple IDs
await userRepository.delete([1, 2, 3]);
// Soft delete (sets deletedAt)
await userRepository.softDelete(1);
// Soft remove entity instance
const user = await userRepository.findOne({ where: { id: 1 } });
await userRepository.softRemove(user);
// Find including soft deleted
const allUsers = await userRepository.find({
withDeleted: true,
});
// Restore soft deleted
await userRepository.restore(1);

src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
// CREATE
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.userRepository.create(createUserDto);
return this.userRepository.save(user);
}
// READ - All
async findAll(page = 1, limit = 10): Promise<{ data: User[]; total: number }> {
const [data, total] = await this.userRepository.findAndCount({
skip: (page - 1) * limit,
take: limit,
order: { createdAt: 'DESC' },
});
return { data, total };
}
// READ - One
async findOne(id: number): Promise<User> {
const user = await this.userRepository.findOne({
where: { id },
relations: ['posts'],
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
// UPDATE
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.userRepository.preload({
id,
...updateUserDto,
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return this.userRepository.save(user);
}
// DELETE
async remove(id: number): Promise<void> {
const user = await this.findOne(id);
await this.userRepository.remove(user);
}
// SOFT DELETE
async softDelete(id: number): Promise<void> {
const user = await this.findOne(id);
await this.userRepository.softRemove(user);
}
// RESTORE
async restore(id: number): Promise<User> {
await this.userRepository.restore(id);
return this.findOne(id);
}
}

For more complex operations, use QueryBuilder.

// Create with QueryBuilder
await userRepository
.createQueryBuilder()
.insert()
.into(User)
.values([
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' },
])
.execute();
// Read with QueryBuilder
const users = await userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.posts', 'post')
.where('user.isActive = :active', { active: true })
.orderBy('user.createdAt', 'DESC')
.getMany();
// Update with QueryBuilder
await userRepository
.createQueryBuilder()
.update(User)
.set({ isActive: false })
.where('lastLoginAt < :date', { date: new Date('2023-01-01') })
.execute();
// Delete with QueryBuilder
await userRepository
.createQueryBuilder()
.delete()
.from(User)
.where('isActive = :active', { active: false })
.execute();

CRUD Best Practices
+------------------------------------------------------------------+
| |
| 1. Use save() for most operations |
| - Handles both insert and update |
| - Works with relations |
| |
| 2. Use insert() for bulk inserts |
| - More efficient for large datasets |
| - No entity overhead |
| |
| 3. Use update() for simple updates |
| - No need to fetch entity first |
| - More efficient |
| |
| 4. Always check if entity exists |
| - Use findOneOrFail() or check null |
| - Throw appropriate exceptions |
| |
| 5. Use transactions for related operations |
| - Ensure data consistency |
| - Rollback on failure |
| |
| 6. Use soft delete for important data |
| - Preserve data for recovery |
| - Maintain audit trail |
| |
+------------------------------------------------------------------+

import { EntityNotFoundError, QueryFailedError } from 'typeorm';
async function safeUpdate(id: number, data: UpdateUserDto) {
try {
const user = await userRepository.preload({ id, ...data });
if (!user) {
throw new NotFoundException('User not found');
}
return await userRepository.save(user);
} catch (error) {
if (error instanceof QueryFailedError) {
// Handle database errors
const driverError = error.driverError;
if (driverError.code === '23505') {
throw new ConflictException('Email already exists');
}
throw new InternalServerErrorException('Database error');
}
throw error;
}
}

CRUD Operations Quick Reference
+------------------------------------------------------------------+
| |
| Operation | Method | Returns |
| -------------------|--------------------|-------------------- |
| Create | save(entity) | Entity |
| Create | insert(data) | InsertResult |
| Read All | find(options) | Entity[] |
| Read One | findOne(options) | Entity | null |
| Read One | findOneOrFail() | Entity |
| Read Count | count(options) | number |
| Update | save(entity) | Entity |
| Update | update(cond, data) | UpdateResult |
| Delete | remove(entity) | Entity |
| Delete | delete(condition) | DeleteResult |
| Soft Delete | softRemove(entity) | Entity |
| Soft Delete | softDelete(cond) | UpdateResult |
| Restore | restore(condition) | UpdateResult |
| |
+------------------------------------------------------------------+

Chapter 13: Find Options & Querying


Last Updated: February 2026