I am building a NestJS RESTful API for a library. I have a Book
entity and a Category
entity. Each book has a category.
Book
entity
@Entity()
export class Book {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
author: string;
@ManyToOne(() => Category, (category) => category.id)
category: Category;
@ManyToOne(() => User)
@JoinColumn({ name: 'createdBy', referencedColumnName: 'id' })
user: User;
@Column()
createdBy: number;
}
Category
entity
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@OneToMany(() => Book, (book) => book.category)
books: Book[];
}
This is the DTO to create a book:
export class CreateBookDto {
@IsString()
@MinLength(3)
title: string;
@IsString()
@MinLength(3)
author: string;
@IsString()
category: string;
}
This is the function that creates a new book:
async create(createBookDto: CreateBookDto, user: ActiveUserInterface) {
const category = await this.validateCategory(createBookDto.category);
return await this.bookRepository.save({
...createBookDto,
category: category,
createdBy: user.sub,
});
}
And this is the function that validates categories:
private async validateCategory(category: string) {
const categoryEntity = await this.categoryRepository.findOneBy({
name: category,
});
if (!categoryEntity) {
throw new NotFoundException('Category not found');
}
return categoryEntity;
}
My question is: why do I need to add again the category on the function to save the book on the DB if it's already defined on the DTO? Actually it works as I expect, but why don't they overlap each other at the time it's saved on the DB?
I tried to create a new book, and it works as expected. I just don't understand why does it work.
Because the category that's defined on your CreateBookDto
is a string value, when you run your validateCategory
function it returns a Category object which is used to create the relationship between Book and Category. If you pass a string instead of the category object it won't work. We need to use the Category object because it contains the category id
which is need to create the relationship between them.
You are not overlapping the values, you are setting a new value to the category
property in the object that will be saved. You could say you are overwriting the previous category value with a new one.
If you want to avoid setting the category again you should set the dto this way and pass a Category
object instead of a string:
export class CreateBookDto {
@IsString()
@MinLength(3)
title: string;
@IsString()
@MinLength(3)
author: string;
// You need a category dto object with it's own decorators
category: CategoryDto;
}