Custom repositories

You can create a custom repository which should contain methods to work with your database.Usually custom repositories are created for a single entity and contains its specific queries.For example, let’s say we want to have a method called findByName(firstName: string, lastName: string)which will search for users by a given first and last names.The best place for this method is in Repository,so we could call it like userRepository.findByName(...).You can achieve this using custom repositories.

There are several ways how custom repositories can be created.

Custom repository extends standard Repository

The first way to create a custom repository is to extend Repository.Example:

  1. import {EntityRepository, Repository} from "typeorm";
  2. import {User} from "../entity/User";
  3. @EntityRepository(User)
  4. export class UserRepository extends Repository<User> {
  5. findByName(firstName: string, lastName: string) {
  6. return this.findOne({ firstName, lastName });
  7. }
  8. }

Then you can use it this way:

  1. import {getCustomRepository} from "typeorm";
  2. import {UserRepository} from "./repository/UserRepository";
  3. const userRepository = getCustomRepository(UserRepository); // or connection.getCustomRepository or manager.getCustomRepository()
  4. const user = userRepository.create(); // same as const user = new User();
  5. user.firstName = "Timber";
  6. user.lastName = "Saw";
  7. await userRepository.save(user);
  8. const timber = await userRepository.findByName("Timber", "Saw");

As you can see you can “get” the repository using getCustomRepositoryand you can access any method created inside it and any method in the standard entity repository.

Custom repository extends standard AbstractRepository

The second way to create a custom repository is to extend AbstractRepository:

  1. import {EntityRepository, AbstractRepository} from "typeorm";
  2. import {User} from "../entity/User";
  3. @EntityRepository(User)
  4. export class UserRepository extends AbstractRepository<User> {
  5. createAndSave(firstName: string, lastName: string) {
  6. const user = new User();
  7. user.firstName = firstName;
  8. user.lastName = lastName;
  9. return this.manager.save(user);
  10. }
  11. findByName(firstName: string, lastName: string) {
  12. return this.repository.findOne({ firstName, lastName });
  13. }
  14. }

Then you can use it this way:

  1. import {getCustomRepository} from "typeorm";
  2. import {UserRepository} from "./repository/UserRepository";
  3. const userRepository = getCustomRepository(UserRepository); // or connection.getCustomRepository or manager.getCustomRepository()
  4. await userRepository.createAndSave("Timber", "Saw");
  5. const timber = await userRepository.findByName("Timber", "Saw");

The difference between this type of repository and the previous one is that it does not expose all the methods Repository has.AbstractRepository does not have any public methods,it only has protected methods, like manager and repository, which you can use in your ownpublic methods.Extending AbstractRepository is useful if you don’t want to expose all methods the standard Repository has to the public.

Custom repository without extends

The third way to create a repository is to not extend anything,but define a constructor which always accepts an entity manager instance:

  1. import {EntityRepository, Repository, EntityManager} from "typeorm";
  2. import {User} from "../entity/User";
  3. @EntityRepository()
  4. export class UserRepository {
  5. constructor(private manager: EntityManager) {
  6. }
  7. createAndSave(firstName: string, lastName: string) {
  8. const user = new User();
  9. user.firstName = firstName;
  10. user.lastName = lastName;
  11. return this.manager.save(user);
  12. }
  13. findByName(firstName: string, lastName: string) {
  14. return this.manager.findOne(User, { firstName, lastName });
  15. }
  16. }

Then you can use it this way:

  1. import {getCustomRepository} from "typeorm";
  2. import {UserRepository} from "./repository/UserRepository";
  3. const userRepository = getCustomRepository(UserRepository); // or connection.getCustomRepository or manager.getCustomRepository()
  4. await userRepository.createAndSave("Timber", "Saw");
  5. const timber = await userRepository.findByName("Timber", "Saw");

This type of repository does not extend anything - you only need to define a constructorwhich must accept EntityManager. Then you can use it everywhere in your repository methods.Also, this type of repository is not bound to a specific entity,thus, you can operate with multiple entities inside them.

Using custom repositories in transactions or why custom repositories cannot be services

Custom repositories cannot be services,because there isn’t a single instance of a custom repository (just like regular repositories or entity manager) in the app.Besides the fact that there can be multiple connections in your app (where entity manager and repositories are different)repositories and managers are different in transactions as well.For example:

  1. await connection.transaction(async manager => {
  2. // in transactions you MUST use manager instance provided by a transaction,
  3. // you cannot use global managers, repositories or custom repositories
  4. // because this manager is exclusive and transactional
  5. // and if let's say we would do custom repository as a service
  6. // it has a "manager" property which should be unique instance of EntityManager
  7. // but there is no global EntityManager instance and cannot be
  8. // thats why custom managers are specific to each EntityManager and cannot be services.
  9. // this also opens opportunity to use custom repositories in transactions without any issues:
  10. const userRepository = manager.getCustomRepository(UserRepository); // DONT USE GLOBAL getCustomRepository here!
  11. await userRepository.createAndSave("Timber", "Saw");
  12. const timber = await userRepository.findByName("Timber", "Saw");
  13. });