Implementing the Repository Pattern with TypeORM
Introduction
Many applications benefit from decoupling the data access layer from the business logic. One common approach is using the Repository pattern. This post explores how to implement the Repository pattern using TypeORM in a TypeScript project.
Setting Up the User Entity
First, define the UserEntity using TypeORM decorators. This entity represents the structure of the data in the database.
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class UserEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({
default: true,
})
isActive: boolean;
}
This UserEntity class is decorated with @Entity(), marking it as a database table. The id property is the primary key, auto-generated. firstName, lastName, and isActive are columns storing user data.
Defining the UserRepository Interface
Next, define an interface for the UserRepository. This interface specifies the contract for interacting with UserEntity objects.
export interface UserRepository {
findById(id: number): Promise<UserEntity | undefined>;
findAll(): Promise<UserEntity[]>;
save(user: UserEntity): Promise<UserEntity>;
delete(id: number): Promise<void>;
}
The UserRepository interface declares methods for common database operations: findById, findAll, save, and delete. These methods abstract away the underlying data access implementation.
Implementing the Repository with TypeORM
Create a class TypeormUserRepository that implements the UserRepository interface, using TypeORM to interact with the database.
import { getRepository } from "typeorm";
import { UserEntity } from "./UserEntity";
import { UserRepository } from "./UserRepository";
export class TypeormUserRepository implements UserRepository {
private userRepository = getRepository(UserEntity);
async findById(id: number): Promise<UserEntity | undefined> {
return this.userRepository.findOne({ where: { id } });
}
async findAll(): Promise<UserEntity[]> {
return this.userRepository.find();
}
async save(user: UserEntity): Promise<UserEntity> {
return this.userRepository.save(user);
}
async delete(id: number): Promise<void> {
await this.userRepository.delete(id);
}
}
This TypeormUserRepository class uses TypeORM's getRepository to interact with the UserEntity. It implements the methods defined in the UserRepository interface, providing a concrete implementation for data access.
Benefits of the Repository Pattern
- Decoupling: Separates business logic from data access implementation.
- Testability: Allows for easy mocking of the repository in unit tests.
- Maintainability: Simplifies changes to the data access layer without affecting business logic.
Conclusion
Using the Repository pattern with TypeORM promotes a clean and maintainable architecture. By abstracting data access behind an interface, you can improve testability and reduce coupling in your application. Consider adopting this pattern to enhance the structure and flexibility of your projects.
Generated with Gitvlg.com