I'm tackling a NestJS course and it's just a bit outdated and at the moment the requirement is to have a "Create" method on the entity class, I can see that the instructor is able to have it functioning but I keep getting an error.
#Entity()
#Unique('task_index_uq', ['title'])
export class Task extends Repository<Task> {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
title: string;
#Column()
description: string;
#Column()
status: TaskStatus;
async createTask(createTaskDto: CreateTaskDto): Promise<Task> {
const { title, description } = createTaskDto;
const task = this.create({
title,
description,
status: TaskStatus.OPEN,
});
await this.save(task);
return task;
}
}
What could be the problem?
Related
I have an entity
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
autogeneratedCode: string;
I'm getting name and string only in my GET request response which is expected.
But when I'm hit my POST Api with body, it is returning name, email, autogeneratedCode as well.
I need to hide autogeneratedCode in all CRUD responses.
Is there anyway to do that? or Am I missing something here?
You can use #Exclude() from 'class-transformer'
Example
import { Exclude } from 'class-transformer';
#Entity()
export class User {
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
#Exclude()
autogeneratedCode: string;
constructor(entity: Partial<User>) {
Object.assign(this, entity);
}
}
Then you can use the constructor to create a new object excluding the #Exclude() properties.
export class UserService {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>
) {}
public async createUser(user: User): Promise<User> {
return new User(
await this.userRepository.save(user)
);
}
}
NestJS Doc on Serialization
https://docs.nestjs.com/techniques/serialization#exclude-properties
Let's say we are constructing our very first entities for a new web application.
There is an entity for users:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Column()
password: string;
#OneToOne(() => Author)
author: Author;
Users can be Authors after registration. So there's a one-to-one relationship between users and authors table. Let's say we have another table called books, an author can have multiple books.
export class Author {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#OneToMany(() => Book, (Book) => Book.Author)
Book: Book[];
#OneToOne(() => User)
#JoinColumn()
User: User;
Here is a sample entity for Books repository:
export class Book {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#Column()
ISIN: string;
#OneToOne(() => Author)
#JoinColumn()
Author: Author;
The question is, when we migrated these entities and built our very first/clean database, how can we insert the data via calling API?
A sample service for API Post method:
#Injectable()
export class BookService {
constructor(
#InjectRepository(Book)
private BookRepository: Repository<Book>,
) {}
async create(createBookDto: CreateBookDto) {
await this.BookRepository.insert(createBookDto);
}
}
And the Controller:
#Controller('books')
export class BookController {
constructor(private readonly BookService: BookService) {}
#Post('/Book')
create(#Body() createBookDto: CreateBookDto) {
return this.BookService.create(createBookDto);
}
}
The problem here is that when I want to create a new book by POSTing data to the API route, it needs the Author to be defined. So how can I post existing user-books-author data into the database via this service?
The best option I think of is to create a new instance of the classes, get the data from request #Body and assign it to the objects of the class then save it to the database.
But I think it's not a good solution, as it's very preferred to use repositories instead of object-class type.
I think you do a mistake in your entity definition. There is OneToMany relation between Author to Book.
Modify your book entity like this
export class Book {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#Column()
ISIN: string;
#ManyToOne(() => Author, author=> author.book, { nullable: true })
#JoinColumn()
Author: Author;
{ nullable: true } this will allow you to save your book data without having an author.
Modify your service to save the book information
public async create(createBookDTO: CreateBookDTO) {
try {
if (
createBookDTO.hasOwnProperty('author') &&
createTaskDTO.author
) {
const author = await this.connection.manager.findOne(Author, {
where: {
id: createBookDTO.author,
},
});
if (author) {
createBookDTO.author = author;
} else {
throw new NotAcceptableException('Author not found');
}
}
return await this.bookRepo.save(createBookDTO);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}
Hello i have problem with relations in NestJS.
I was trying to get user with relation named club like that:
async findOne(id: number): Promise<User> {
return this.userRepository.findOne({
where: { id: id },
relations: ['club'],
});
}
And that throws this error:
ER_PARSE_ERROR: You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE `User`.`id` = 1) `distinct Alias` ORDER BY `User_id` ASC LIMIT 1' at line 1
I think my entites look correct, can it be problem with MariaDB?
There is my User entity:
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
#Unique('email_unique', ['email'])
email: string;
#Column()
#Unique('username_unique', ['username'])
username: string;
#Column()
password: string;
#Column({ type: 'boolean' })
active = false;
#Column()
activationToken: string;
#OneToOne(() => Club, (club) => club.user)
club: Club;
}
And Club entity:
#Entity()
export class Club {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => League)
league: League;
#OneToOne(() => User, (user) => user.club)
user: User;
#Column()
name: string;
#Column()
location: string;
#OneToMany(() => Player, (player) => player.club)
players: Player[];
}
You are missing a JoinColumn annotation - it could be the reason for the erroneous generated sql. Try defining the JoinColumn on the owning side, e.g.
export class User {
// ...
#OneToOne(() => Club, (club) => club.user)
#JoinColumn()
club: Club;
}
I have two entities: User and Photo with relationship OneToMany. One user can have many photos.
User:
#Entity('users')
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Exclude()
#Column()
password: string;
#Column({ default: '' })
avatar: string;
#OneToMany(() => Photo, photo => photo.user, { cascade: true })
photos: Photo[];
}
And Photo:
#Entity('photos')
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#Column({default: ""})
description: string;
#ManyToOne(() => User, user => user.photos)
user: User;
}
So, my question is: how can i add new photo to specific user? I tried to do something like this, but it doesn't work as expected.
#Patch(':id/photos')
addPhoto(#Param('id') id: number, #Body() createPhotoDto: CreatePhotoDto){
return this.userService.createImage(id, createPhotoDto);
}
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
const newUser = await this.usersRepository.preload({
id: +id,
photos: [...user.photos, createPhotoDto]
});
return this.usersRepository.save(newUser);
}
What I got from the CreatePhotoDto that you save the image and bind it with the user,
one of the easiest way to do it, First declare you imageRepository in your service then :
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
return await this.imageRepository.save({url:createPhotoDto.url,user:user});
// if you want to return the user with the image added you make another find with the relation exp:
// return await this.usersRepository.findOne(id,{ relations: ['photos'],});
}
So i've been struggling with this for some time now, i have this many to many realtionship in my codebase. Between a User and Odds. I want the User to be able to add an Odds
User Entity:
#Entity()
#Unique(['username'])
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Unique(['email'])
#Column()
email: string;
#Column()
password: string;
#Column({ nullable: true })
customerId: string;
#ManyToMany(type => Odds, odds => odds.user)
#JoinTable()
odds: Odds[];
#Column()
salt: string;
async validatePassword(password: string): Promise<boolean> {
const hash = await bcrypt.hash(password, this.salt);
return hash === this.password;
}
}
Odds Entity:
#Entity()
export class Odds extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
hometeam: string;
#Column()
awayteam: string;
#Column()
hometeamLogo: string;
#Column()
awayteamLogo: string;
#Column()
bet: string;
#Column({ type: 'real' })
value: string;
#Column()
stake: number;
#Column({ type: 'real' })
bookieOdds: string;
#ManyToMany(type => User, user => user.odds)
user: User[];
}
When i try to add a relation like this
async addOddsToUser(user: User, oddsId: number): Promise<void> {
const id = user.id;
const userDB = await this.userRepository.findOne({ id });
const odds = await this.oddsRepository.findOne({ id: oddsId });
userDB.odds = [odds];
userDB.save();
}
In the associative table it does add the relation the first time, but if i add one more it overrides the first relation, i also tried userDB.odds.push(odds); which doesnt work.
Any help would be appreciated!
The Problem is that your addOddsToUser function overrides the userDB.odds array with a new single array item each time. Thus existing relations will be deleted as mentioned in the FAQ:
When you save the object it will check if there are any categories in the database bind to the question - and it will detach all of them. Why? Because relation equal to [] or any items inside it will be considered like something was removed from it, there is no other way to check if an object was removed from entity or not.
Therefore, saving an object like this will bring you problems - it will remove all previously set categories.