For a long time, TypeORM seemed to have a reputation of being somewhat stagnant. Lately, they stepped up their game, though, and started releasing many new versions. When doing that, they introduced a large number of breaking changes. In this article, we go through the most significant differences between 0.2.x and 0.3.x versions so far and see how they affect NestJS projects. When writing this article, I was using TypeORM 0.3.7 and @nestjs/typeorm 8.1.4 Changes made to the repository API TypeORM made a few changes to the functions we use to query the data. Changes to the findOne method First, they got rid of the findOne() function returning the first row from the table when not provided with an argument. It was caused by findOne(undefined) being confusing. What’s more, TypeORM dropped the support for using findOne(id) to make the syntax more consistent. So now, we can only use findOne() by providing it with an object. Because of all of the above, the following code no longer works:this.categoriesRepository.findOne( id, { relations: ['posts'], withDeleted: true } ); error TS2554: Expected 1 arguments, but got 2. Instead, we must pass a single object to the findOne() method.this.categoriesRepository.findOne( { where: { id }, relations: ['posts'], withDeleted: true } ); To see all of the available options, check out the FindOneOptions interface. Although the above code with relations: ['posts'] still works, it is now deprecated and will soon be removed. Instead, we should use a new object-literal notation.this.categoriesRepository.findOne( { where: { id }, relations: { posts: true }, withDeleted: true } );If we want to load a nested relation, we can create an object like this one:relations: { posts: { author: true } } Changes to the find method TypeORM made a similar set of changes to the find() method. Because of that, the following code is no longer valid:this.commentsRepository.find({ post: { id: query.postId } }); error TS2345: Argument of type { post: { id: number; }; } is not assignable to parameter of type FindManyOptions. Instead, we need to provide the where object explicitly.this.commentsRepository.find({ where: { post: { id: query.postId } } }); Besides the findOne and find methods, similar changes happened to findOneOrFail, count, and findAndCount. Using the new findOneBy and findBy functions When querying data, we don’t always need additional options, such as relations. In that case, we can use the new findOneBy method.this.databaseFilesRepository.findOneBy({ id: fileId });Similarly, we can use the new findBy method if we want to find multiple entities and don’t need to provide additional options such as relations.this.commentsRepository.findBy({ post: { id: query.postId } }); Besides the above, we also have new findOneByOrFail, countBy, and findAndCountBy functions. Deprecating the findByIds method The findByIds is now deprecated and will soon be removed. Instead, we can use the findBy method with the In operator.import { In } from 'typeorm'; postsRepository.findBy({ id: In([1, 2, 3]) }) Renaming Connection to DataSource Before TypeORM 0.3.0, the configuration with our database used to be called a Connection. Recently, TypeORM renamed it to DataSource. Throughout this series, we didn’t interact with the Connection much except for working with transactions. users.service.ts import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, Connection } from 'typeorm'; import User from './user.entity'; import { FilesService } from '../files/files.service'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository, private readonly filesService: FilesService, private connection: Connection ) {} async deleteAvatar(userId: number) { const queryRunner = this.connection.createQueryRunner(); const user = await this.getById(userId); const fileId = user.avatar?.id; if (fileId) { await queryRunner.connect(); await queryRunner.startTransaction(); try { await queryRunner.manager.update(User, userId, { ...user, avatar: null }); await this.filesService.deletePublicFileWithQueryRunner(fileId, queryRunner); await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw new InternalServerErrorException(); } finally { await queryRunner.release(); } } } // ... }Unfortunately, the above code is no longer valid. Instead of Connection, we should use DataSource instead. users.service.ts import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; import User from './user.entity'; import { FilesService } from '../files/files.service'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository, private readonly filesService: FilesService, private dataSource: DataSource ) {} async deleteAvatar(userId: number) { const queryRunner = this.dataSource.createQueryRunner(); const user = await this.getById(userId); const fileId = user.avatar?.id; if (fileId) { await queryRunner.connect(); await queryRunner.startTransaction(); try { await queryRunner.manager.update(User, userId, { ...user, avatar: null }); await this.filesService.deletePublicFileWithQueryRunner(fileId, queryRunner); await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw new InternalServerErrorException(); } finally { await queryRunner.release(); } } } } Changes to the configuration TypeORM made a few significant changes to how we configure our database connection. First, let’s take a look at our current configuration. database.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; @Module({ imports: [ TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ type: 'postgres', username: configService.get('POSTGRES_USER'), password: configService.get('POSTGRES_PASSWORD'), // ... entities: [ __dirname + '/../**/*.entity{.ts,.js}', ], }) }), ], }) export class DatabaseModule {}Unfortunately, strings with entities, migrations, and subscribers is now deprecated. Therefore, in future TypeORM versions, we will be able only to use entity references. database.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; import Post from '../posts/post.entity'; import User from '../users/user.entity'; @Module({ imports: [ TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ type: 'postgres', username: configService.get('POSTGRES_USER'), password: configService.get('POSTGRES_PASSWORD'), // ... entities: [ Post, User, // ... ], }) }), ], }) export class DatabaseModule {}Having to list all of our entities might be a bit troublesome. Fortunately, @nestjs/typeorm implements the autoLoadEntities option that we can use to auto-load entities. database.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; import Address from '../users/address.entity'; @Module({ imports: [ TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ type: 'postgres', username: configService.get('POSTGRES_USER'), password: configService.get('POSTGRES_PASSWORD'), entities: [ Address ], autoLoadEntities: true, // ... }) }), ], }) export class DatabaseModule {}Please notice that we still include the Address entity in the entities array above. The above is because we need to manually add every entity we don’t use through TypeOrmModule.forFeature(). In our case, a good example is the Address entity we use only through a one-to-one relationship. New array operators Previously, we had to use raw SQL to run more advanced queries with PostgreSQL arrays.async getPostsWithParagraph(paragraph: string) { return this.postsRepository .query(`SELECT * from post WHERE ${paragraph} = ANY(paragraphs)`); }Now, TypeORM 0.3.1 introduced new array operators that can let us handle such cases.import { ArrayContains } from 'typeorm'; async getPostsWithParagraph(paragraph: string) { return this.postsRepository .findBy({ paragraphs: ArrayContains([paragraph]) }) } Besides the above, we also have the ArrayContainedBy and ArrayOverlap operators. Other changes Besides all the changes mentioned so far, TypeORM fixed a ton of minor and significant issues and added performance improvements. It is worth tracking the changelog in the GitHub releases page. Some of the important changes are: requiring NodeJS 14+ supporting