Interview

10 TypeORM Interview Questions and Answers

Prepare for your next technical interview with this guide on TypeORM, featuring common questions and answers to enhance your database management skills.

TypeORM is a powerful and flexible Object-Relational Mapper (ORM) for TypeScript and JavaScript. It simplifies database interactions by allowing developers to work with database entities as if they were regular TypeScript or JavaScript objects. TypeORM supports multiple database systems, including MySQL, PostgreSQL, SQLite, and more, making it a versatile choice for various projects.

This article provides a curated selection of TypeORM interview questions designed to help you demonstrate your proficiency and understanding of this ORM. By reviewing these questions and their answers, you can better prepare for technical interviews and showcase your ability to effectively manage database operations using TypeORM.

TypeORM Interview Questions and Answers

1. Write a basic entity definition for a User with fields id, name, and email.

To define a basic entity in TypeORM for a User with fields id, name, and email, you can use the following code snippet. This example uses TypeScript, which is commonly used with TypeORM.

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    email: string;
}

2. How do you establish a one-to-many relationship between two entities? Provide an example.

In TypeORM, a one-to-many relationship is established between two entities when one entity can be associated with multiple instances of another entity. This is typically done using the @OneToMany and @ManyToOne decorators. The @OneToMany decorator is used on the entity that holds the collection, while the @ManyToOne decorator is used on the entity that holds the reference to the single instance.

Example:

import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @OneToMany(() => Post, post => post.user)
    posts: Post[];
}

@Entity()
export class Post {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @ManyToOne(() => User, user => user.posts)
    user: User;
}

In this example, the User entity has a one-to-many relationship with the Post entity. The User entity uses the @OneToMany decorator to indicate that it can have multiple posts, while the Post entity uses the @ManyToOne decorator to indicate that each post belongs to a single user.

3. Write a query to find all users whose name starts with ‘A’.

To find all users whose name starts with ‘A’ using TypeORM, you can utilize the find method along with a where clause.

Here is an example of how you can achieve this:

import { getRepository } from 'typeorm';
import { User } from './entity/User';

async function findUsersStartingWithA() {
    const userRepository = getRepository(User);
    const users = await userRepository.find({
        where: {
            name: Like('A%')
        }
    });
    return users;
}

In this example, the Like operator is used to match any user whose name starts with ‘A’. The getRepository function is used to get the repository for the User entity, and the find method is used to perform the query.

4. Write a migration script to add a new column age to the User entity.

To add a new column age to the User entity, you would create a migration script. Below is an example of how to achieve this:

import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";

export class AddAgeColumnToUser1634567890123 implements MigrationInterface {
    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.addColumn("user", new TableColumn({
            name: "age",
            type: "int",
            isNullable: true
        }));
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.dropColumn("user", "age");
    }
}

In this example, the up method is used to define the changes to be applied to the database, which in this case is adding a new column age to the user table. The down method is used to revert these changes, ensuring that the migration can be rolled back if necessary.

5. How do you handle transactions? Provide a code example.

In TypeORM, transactions are used to ensure that a series of database operations are executed in a way that maintains data integrity.

Here is an example of how to handle transactions in TypeORM:

import { getManager } from "typeorm";

async function transactionalOperation() {
    await getManager().transaction(async transactionalEntityManager => {
        // Perform your database operations here
        await transactionalEntityManager.save(entity1);
        await transactionalEntityManager.save(entity2);
        // Add more operations as needed
    });
}

In this example, the transaction method of getManager is used to execute a series of operations within a transaction. If any of the operations fail, the entire transaction is rolled back.

6. Explain how to use custom repositories and why you might need them.

Custom repositories in TypeORM allow you to extend the default repository functionality by adding custom methods and encapsulating complex query logic. This is particularly useful when you have specific business logic that needs to be reused across different parts of your application.

To create a custom repository, you need to extend the Repository class and define your custom methods. You can then use this custom repository in your services or controllers.

Example:

import { EntityRepository, Repository } from 'typeorm';
import { User } from '../entity/User';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
    findByName(firstName: string, lastName: string) {
        return this.findOne({ where: { firstName, lastName } });
    }
}

// Usage in a service
import { getCustomRepository } from 'typeorm';
import { UserRepository } from '../repository/UserRepository';

const userRepository = getCustomRepository(UserRepository);
const user = await userRepository.findByName('John', 'Doe');

7. Write a custom repository method to find users by their email domain (e.g., all users with emails ending in ‘@example.com’).

To create a custom repository method to find users by their email domain, you can extend the base repository and add a method that uses a query to filter users based on the email domain.

Example:

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
    async findByEmailDomain(domain: string): Promise<User[]> {
        return this.createQueryBuilder('user')
            .where('user.email LIKE :domain', { domain: `%@${domain}` })
            .getMany();
    }
}

In this example, the findByEmailDomain method constructs a query to find users whose email addresses end with the specified domain. The LIKE operator is used to match the email pattern.

8. Provide an example of how to use TypeORM with a NestJS application.

To use TypeORM with a NestJS application, you need to follow these steps:

1. Install the necessary packages.
2. Configure the TypeORM module.
3. Create an entity.
4. Use a repository to perform database operations.

First, install the required packages:

npm install @nestjs/typeorm typeorm mysql2

Next, configure the TypeORM module in your app.module.ts:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { User } from './user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'test',
      entities: [User],
      synchronize: true,
    }),
    TypeOrmModule.forFeature([User]),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Create an entity user.entity.ts:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  age: number;
}

Finally, use a repository to perform database operations in a service:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

  async createUser(name: string, age: number): Promise<User> {
    const user = this.userRepository.create({ name, age });
    return this.userRepository.save(user);
  }

  async findAll(): Promise<User[]> {
    return this.userRepository.find();
  }
}

9. Explain the role of decorators in TypeORM and provide examples.

Decorators in TypeORM are used to define the structure of the database schema directly within the entity classes. They provide a way to annotate classes and their properties to specify how they should be mapped to the database. This approach makes the code more readable and maintainable.

For example, the @Entity decorator is used to mark a class as a database table, while the @Column decorator is used to mark a property as a table column. Other decorators like @PrimaryGeneratedColumn and @ManyToOne are used to define primary keys and relationships between tables, respectively.

Example:

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';

@Entity()
class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToOne(() => Role, role => role.users)
    role: Role;
}

@Entity()
class Role {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @OneToMany(() => User, user => user.role)
    users: User[];
}

In this example, the User class is marked as an entity with the @Entity decorator. The id property is marked as a primary key with the @PrimaryGeneratedColumn decorator, and the name property is marked as a column with the @Column decorator. The role property is marked with the @ManyToOne decorator to indicate a many-to-one relationship with the Role entity.

10. Describe how to test TypeORM entities and repositories.

Testing TypeORM entities and repositories involves several steps to ensure that your database interactions are functioning correctly. The process generally includes setting up a test database, configuring TypeORM to use this test database, and writing unit tests using a testing framework like Jest.

First, you need to configure a test database. This can be an in-memory database like SQLite for faster tests or a separate instance of your actual database. You will then configure TypeORM to connect to this test database during testing.

Next, you write unit tests for your entities and repositories. These tests should cover CRUD operations and any custom queries or methods you have implemented. Using Jest, you can mock dependencies and focus on testing the logic within your repositories.

Example:

import { createConnection, getRepository } from 'typeorm';
import { User } from './entities/User';
import { UserRepository } from './repositories/UserRepository';

beforeAll(async () => {
    await createConnection({
        type: 'sqlite',
        database: ':memory:',
        dropSchema: true,
        entities: [User],
        synchronize: true,
        logging: false,
    });
});

afterAll(async () => {
    const connection = getConnection();
    await connection.close();
});

test('should create a new user', async () => {
    const userRepository = getRepository(User);
    const user = new User();
    user.name = 'John Doe';
    user.email = '[email protected]';

    await userRepository.save(user);
    const savedUser = await userRepository.findOne({ email: '[email protected]' });

    expect(savedUser).toBeDefined();
    expect(savedUser.name).toBe('John Doe');
});
Previous

10 File Management Interview Questions and Answers

Back to Interview
Next

10 RestTemplate Interview Questions and Answers