Skip to main content

Database Operations

We've connected to the database and defined our Entity. Now we can perform CRUD operations using TypeORM's Repository API.

The Repository

In TypeORM, a Repository is the interface between your application and a specific database table. You get a repository for an entity like this:

const personRepository = AppDataSource.getRepository(Person);

The repository gives you methods to save, find, update, and delete rows — without writing SQL.

info

Record, Row, and Document all mean the same thing in different contexts. In PostgreSQL, we call it a row or record. In MongoDB, it's a document. TypeORM works with instances of your Entity class.

Create (Insert)

save()

save() is the most flexible method. It creates a new row if the entity has no id, or updates an existing row if it does.

Creating a new person
const person = personRepository.create({
name: "Rizwan Ashiq",
age: 25,
email: "rizwan@example.com",
gender: "male",
});

const saved = await personRepository.save(person);

console.log(saved);
// { id: 1, name: 'Rizwan Ashiq', age: 25, email: 'rizwan@example.com', ... }

Note: create() builds the entity object in memory. save() actually writes it to the database.

You can also use save() directly with a plain object:

const saved = await personRepository.save({
name: "Ashiq Ashiq",
age: 30,
email: "ashiq@example.com",
gender: "female",
});

insert()

insert() is a low-level bulk insert. It does not go through entity lifecycle hooks and is faster for large inserts.

Inserting multiple rows
await personRepository.insert([
{ name: "Person 1", age: 20, email: "p1@example.com", gender: "male" },
{ name: "Person 2", age: 22, email: "p2@example.com", gender: "female" },
]);

Read (Select)

find()

Returns all rows that match the given conditions. Returns an empty array if none match.

All Rows

Get all persons
const persons = await personRepository.find();

console.log(persons); // [{ id: 1, name: 'Rizwan', ... }, ...]

Filtered Rows

Pass a where condition to filter:

Get persons by country
const persons = await personRepository.find({
where: { gender: "male" },
});

You can use multiple conditions (acts as AND):

Get persons by multiple fields
const persons = await personRepository.find({
where: { gender: "male", isAlive: true },
});

Sorting

Get persons sorted by age ascending
const persons = await personRepository.find({
order: { age: "ASC" },
});

Limiting Results

const persons = await personRepository.find({
take: 10, // LIMIT 10
skip: 0, // OFFSET 0
});

Loading Relations

To load related entities (like populating in Mongoose), use relations:

Get posts with their authors
const posts = await postRepository.find({
relations: { author: true },
});

// post.author will be the full User object

findOne()

Returns the first row that matches, or null if none found.

Get one person by email
const person = await personRepository.findOne({
where: { email: "rizwan@example.com" },
});

if (!person) {
// handle not found
}

findOneBy()

A shorthand for findOne({ where: ... }):

const person = await personRepository.findOneBy({ id: 1 });

Advanced Queries with FindOperators

TypeORM provides operators for more complex where conditions, imported from typeorm:

import { Like, MoreThan, LessThan, In, Not, IsNull } from "typeorm";
OperatorDescriptionExample
LikeSQL LIKE pattern matchwhere: { name: Like("%rizwan%") }
ILikeCase-insensitive LIKEwhere: { name: ILike("%rizwan%") }
MoreThanGreater thanwhere: { age: MoreThan(18) }
LessThanLess thanwhere: { age: LessThan(60) }
BetweenBetween two valueswhere: { age: Between(18, 30) }
InValue in an arraywhere: { gender: In(["male", "female"]) }
NotNegate a conditionwhere: { gender: Not("other") }
IsNullColumn is NULLwhere: { phone: IsNull() }

Example:

Find adults with a name matching a pattern
import { MoreThan, ILike } from "typeorm";

const persons = await personRepository.find({
where: {
age: MoreThan(18),
name: ILike("%rizwan%"),
},
});

count()

Returns the number of rows matching the condition:

const totalAdults = await personRepository.count({
where: { age: MoreThan(18) },
});

Update

save() — Update an Existing Row

The easiest way to update is to fetch the row, modify it, and call save(). TypeORM detects the id and issues an UPDATE instead of INSERT.

Update a person's age
const person = await personRepository.findOneBy({ id: 1 });

if (person) {
person.age = 26;
await personRepository.save(person);
}

update()

update() lets you update rows by criteria without fetching them first. It is faster but does not trigger TypeORM lifecycle hooks.

Update age by id
await personRepository.update({ id: 1 }, { age: 27 });

// Or shorthand with just the id:
await personRepository.update(1, { age: 27 });

It does not return the updated entity. If you need the updated row, fetch it separately:

await personRepository.update(1, { age: 27 });
const updated = await personRepository.findOneBy({ id: 1 });

Partial Update vs Full Replace

save() only updates the fields you set on the object. update() only updates the fields you pass in the second argument. Neither of these replaces the whole row (unlike replaceOne in Mongoose). To replace all columns, fetch the entity and reassign all fields before saving.

Delete

delete()

Deletes rows by condition or by ID.

Delete by id
await personRepository.delete(1);

// Or by criteria:
await personRepository.delete({ gender: "other" });

delete() returns a result object with affected — the number of rows deleted.

const result = await personRepository.delete(1);
console.log(result.affected); // 1
danger

Calling delete({}) with an empty object will throw an error in TypeORM (to prevent accidental deletion of all rows). To delete all rows, use personRepository.clear().

remove()

remove() takes an entity instance (or array of instances) and deletes them. It goes through lifecycle hooks.

Remove a fetched entity
const person = await personRepository.findOneBy({ id: 1 });

if (person) {
await personRepository.remove(person);
}

Soft Delete

If your entity has a @DeleteDateColumn(), you can use soft deletes instead of permanently removing rows:

await personRepository.softDelete(1);

The row remains in the database but has a deletedAt timestamp. Regular find() calls automatically exclude soft-deleted rows. To include them:

const allIncludeDeleted = await personRepository.find({
withDeleted: true,
});

To restore a soft-deleted row:

await personRepository.restore(1);

Conclusion

In this doc, we learned how to perform CRUD operations in TypeORM using the Repository API. We covered:

  • save() and insert() for creating rows
  • find(), findOne(), findOneBy() and operators for reading rows
  • save() and update() for modifying rows
  • delete(), remove(), and soft deletes for removing rows

In the next doc, we'll put it all together in a full Express + TypeORM example.