I have a couple of questions about NestJS and TypeOrm.
First, how to pass an array of strings to DTO? I tried just to use :string[] type, but the compiler gives an error.
This is my Post entity:
#Entity('posts')
export class Post {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => User, user => user.posts, { cascade: true })
author: number;
#Column({ type: 'timestamp' })
date: Date;
#Column()
text: string;
#Column({ default: 0 })
likes: number;
#OneToMany(() => Photo, photo => photo.post, { cascade: true })
photos: Photo[];
}
And CreatePostDto:
export class CreatePostDto {
authorId: number;
date: Date;
text?: string;
// photos?: string[];
}
And the second question: How can i save to the repository every photo (keeping the connection with post), posts to the posts repo and update user by adding new post binded to him.
I tried something like this, but it won't work obviously.
async create(createPostDto: CreatePostDto) {
const post = this.postsRepository.create(createPostDto);
const user = await this.usersRepository.findOne(createPostDto.authorId);
return this.postsRepository.save({author: user, date: createPostDto.date, text: createPostDto.text});
}
What you missed here is saving photos before bind them with the post, here's an example:
async create(createPostDto: CreatePostDto) {
let photos:Array<Photo> = [] ; array of type photo entity
for(let urlPhoto of createPostDto.photos)
{
let photo = await this.imageRepository.save({url : urlPhoto }); you must save the photos first
photos.push(photo);
}
const user = await this.usersRepository.findOne(createPostDto.authorId);
return this.postsRepository.save({author: user, date: createPostDto.date, text:
createPostDto.text,photos:photos});
}
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number
#Column("simple-array")
names: string[]
}
Related
I have four tables users, ingredients, recipes and a connection table ingredients_ingrec_recipes that TypeORM created automatically. This connection table consists of two columns: ingredientsId and recipesId.
In the ingredient.entity.ts file I have defined #ManyToMany relation with { eager:true } between ingredients and recipes. When I make a Get-Request for a specific ingredient (via Postman), I also see the associated recipes in the response automatically, thanks to the magic of { eager: true } and TypeORM+NestJS.
Now I need exactly the same, but for recipes (other side). In the recipe response, the corresponding ingredients should also be displayed. Unfortunately, according to TypeORM, I can only use #ManyToMany with { eager: true } in one of the two entities. In order to achieve the same with recipes, I have to do this somehow via leftJoinAndSelect(). However, I don't know how to do this via this connection table ingredients_ingrec_recipes in my code.
Here is my code:
ingredient.entitiy.ts
#Entity({ name: 'ingredients' })
export class IngredientEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
slug: string;
#Column()
name: string;
#Column({ default: '' })
description: string;
// more fields
#ManyToMany(() => RecipeEntity, { eager: true })
#JoinTable()
ingrec: RecipeEntity[];
#ManyToOne(() => UserEntity, (user) => user.recipes, { eager: true })
author: UserEntity;
}
recipe.entity.ts
#Entity({ name: 'recipes' })
export class RecipeEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
slug: string;
#Column()
title: string;
#Column({ default: '' })
description: string;
#Column({ default: '' })
body: string;
// more fields
#ManyToOne(() => UserEntity, (user) => user.recipes, { eager: true })
author: UserEntity;
}
recipe.service.ts
async findAll(query: any) {
const queryBuilder = getRepository(RecipeEntity)
.createQueryBuilder('recipes')
.leftJoinAndSelect('recipes.author', 'author');
queryBuilder.orderBy('recipes.createdAt', 'DESC');
if (query.author) {
const author = await this.userRepository.findOne({
username: query.author,
});
queryBuilder.andWhere('recipes.authorId = :id', {
id: author.id,
});
}
const recipes = await queryBuilder.getMany();
return { recipes };
}
I have already used leftJoinAndSelect() with the author, but in a #OneToMany relationship. How can I adjust my findAll() service function so that I can still see in my recipe-response the data from my user (author) table and also the associated data from the ingredients?
ingredientsId
recipesId
3
1
1
2
3
3
3
4
If I add following to the RecipeEntity:
#ManyToMany(() => IngredientEntity, { eager: true })
ingrec: IngredientEntity[];
I get an server error and [Nest] 230 - 05/17/2022, 3:02:11 PM ERROR [ExceptionsHandler] Maximum call stack size exceeded
Without { eager: true } I don't get any errors but I also don't see any data from the ingredients table.
I am new to this typescript, nestjs. I want to know how can I convert Promise<any[]> to Promise<MyEntity[]> so that I can successfully excute this code I created:
const usersfromTransaction = this.repoTransaction
.createQueryBuilder()
.select('user_id', 'id')
.distinctOn(['user_id'])
//.leftJoinAndSelect("user", "user")
.where('EXTRACT(YEAR FROM created_date)=EXTRACT(YEAR FROM NOW())')
.andWhere('extract(month from created_Date)=extract(month from now())')
.limit(10)
.getRawMany();
const users = this.repoUser.find(usersfromTransaction);
return user;
User entity:
#Entity()
export class User {
#PrimaryGeneratedColumn('uuid')
id:string;
#Column({ nullable: true })
firstname:string;
#Column({ nullable: true })
lastname:string;
}
There error is from this line of code this.repoUser.find(usersfromTransaction);
You have to convert it to unknown or any first, like so...
const usersfromTransaction = ......... as unknown as Promise<MyEntity[]>;
I m using NestJS with TypeORM and I m trying to save a user conversation with messages. I set the messages field on the conversation entity to cascade: true. But When I try this code:
const user3: User = { login: 'admin', createdBy: 'system', lastModifiedBy: 'system' };
const user4: User = { login: 'user', createdBy: 'system', lastModifiedBy: 'system' };
const message1: Message = { content: 'Hello How are you? ', createdBy: user3.login, lastModifiedBy: user3.login };
const conversation1: Conversation = { sender: user3, reciever: user4, messages: [message1] };
getConnection().getRepository(Conversation).save(conversation1);
It creates this query:
INSERT INTO "message"("id", "createdBy", "createdDate", "lastModifiedBy", "lastModifiedDate", "content", "conversationSenderId", "conversationRecieverId", "conversationDate") VALUES (?, ?, datetime('now'), ?, datetime('now'), ?, ?, ?, ?)
-- PARAMETERS: ["67348880-6897-47cb-92ef-5f66ffb2e88c","admin","admin","Hello How are you? ","b2420445-c8ee-4080-86b3-98ab12ef576b","09f3672d-9b2f-4f68-b47e-a7ca5d806ec6","2019-10-24 14:41:40.000"]
with this error:
{ [Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed] errno: 19, code: 'SQLITE_CONSTRAINT' }
I can see it's a problem with the foreign key of messages table but I can't fix it because it's TypeORM library which handle the creation, not me.
I searched on internet (documentation, github, stackoverflow) without success.
I think it's bug in TypeORM library but I m a novice in it so I prefer asking if I did something wrong
Here are my entities :
export abstract class BaseEntity {
#ObjectIdColumn()
#PrimaryGeneratedColumn('uuid')
id?: string;
#Column()
createdBy?: string;
#CreateDateColumn()
createdDate?: Date;
#Column()
lastModifiedBy?: string;
#UpdateDateColumn()
lastModifiedDate?: Date;
}
#Entity('user')
export class User extends BaseEntity {
#Column()
login?: string;
#OneToMany(type => Conversation, conversation => conversation.sender)
sentConversations?: Conversation[];
#OneToMany(type => Conversation, conversation => conversation.reciever)
recievedConversations?: Conversation[];
}
#Entity()
export class Conversation {
#PrimaryColumn()
senderId?: string;
#PrimaryColumn()
recieverId?: string;
#CreateDateColumn({ primary: true })
date?: Date;
#OneToMany(type => Message, message => message.conversation, { cascade: true })
messages: Message[];
#ManyToOne(type => User, user => user.sentConversations)
#JoinColumn({ name: 'senderId' })
sender?: User;
#ManyToOne(type => User, user => user.recievedConversations)
#JoinColumn({ name: 'recieverId' })
reciever?: User;
}
#Entity('message')
export class Message extends BaseEntity {
#Column({ length: 10000 })
content: string;
#ManyToOne(type => Conversation, conversation => conversation.messages)
conversation?: Conversation;
}
In this repository, you will be able to reproduce the bug:
https://github.com/youtix/nest-typerorm-test
The foreign key constraint is on your database tables. Cascade in RDBMS terms means its going to propagate through every relationship. I believe your adminID on your creation isn't a user within your user table. You need to query that user first then use it for the message push. This should solve your problem man.
Select * from user where uname = 'admin'
Insert into message table where userId = (userId from 1)
celebrate
I checked your git project, if you change synchronize: false in orm.config.js everything should work fine
I have
#Entity('procedures')
export class ProcedureEntity {
#PrimaryGeneratedColumn()
id: number;
#OneToMany(type => MediaFileEntity, media => media.procedure, {
cascade: true
})
files: MediaFileEntity[];
};
and
#Entity('media_files')
export class MediaFileEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ nullable: true })
deletedAt: Date;
#ManyToOne(type => ProcedureEntity, procedure => procedure.files)
procedure: ProcedureEntity;
};
Does anybody know how do I create a query builder so that I get all the procedures, but with media files that have deletedAt NULL ?
I'm trying to do
const procedures = await this.procedureRepository
.createQueryBuilder("procedure")
.leftJoinAndSelect("procedure.doctor", "doctor")
.leftJoinAndSelect("procedure.patient", "patient")
.innerJoin('procedure.files', 'mediaFile', 'mediaFile.deletedAt IS NULL')
.where("(patient.id = :id OR doctor.id = :doctorId)", { id: user.id, doctorId: user.id })
.andWhere('procedure.deletedAt IS NULL')
.getMany();
but I get Cannot find name 'where'. Which is odd, because this.procedureRepository
.createQueryBuilder("procedure") is also a SelectQueryBuilder<ProcedureEntity> and so is this.procedureRepository
.createQueryBuilder("procedure")
.leftJoinAndSelect("procedure.doctor", "doctor")
.leftJoinAndSelect("procedure.patient", "patient")
.innerJoin('procedure.files', 'mediaFile', 'mediaFile.deletedAt IS NULL');
Thanks in advance!
EDIT: Funny enough, I already had the answer. But I'm gonna leave it here anyway, for posterity 😄
You have a syntax error. You have semicolon after innerJoin. Is this the cause of the error?
#Entity()
export class User {
#PrimaryColumn()
id: string;
#Column({unique: true})
username: string;
#Column({unique: true})
email: string;
#OneToMany(type => Post, post => post.id)
posts: Post[];
}
#Entity()
export class Post {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(type => User, user => user.posts)
#JoinColumn({name: 'user_id'})
user: User;
#OneToMany(type => Image, image => image.id)
images: Image[];
}
#Entity()
export class Image {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(type => Post, post => post.images)
#JoinColumn({name : 'post_id'})
post: Post;
}
I have these 3 entities and I want to make a query to have all the posts from a user and for that post to get all the images. I am trying to do this using the following code:
return await this.postRepository.createQueryBuilder("post")
.innerJoinAndSelect("post.images", "image")
.where("user_id = :userId", {userId: id})
.getMany();
And I get the following error:
Cannot read property 'joinColumns' of undefined
I also tried this instead of the .innerJoin from above:
.innerJoinAndSelect(Image, "image", "image.post_id = post.id")
This way I don't get that error anymore, but as a result I get only the post and I don't get the images from it
I have been struggling with the same problem myself.
You should change the relation here:
#OneToMany(type => Image, image => image.id)
images: Image[];
To this:
#OneToMany(type => Image, image => image.post)
images: Image[];
Notice that image.id should be image.post instead to match the inverse side.
*Edit =>
Over here the same problem appears:
#OneToMany(type => Post, post => post.id)
posts: Post[];
This relation should also have the inverse side, post.id should be post.user instead:
#OneToMany(type => Post, post => post.user)
posts: Post[];
Be careful about this since it doesn't throw any errors until runtime.
I just tested this query with the above modifications and the error is gone:
return await this.postRepository.find({
relations: ['images', 'user'],
where: { user: { id: id } },
});
You may also omit the #JoinColumn() decorators, as they are not necessary in one to many and many to one relationships, you can see it mentioned in the official docs:
You can omit #JoinColumn in a #ManyToOne / #OneToMany relation. #OneToMany cannot exist without #ManyToOne. If you want to use #OneToMany, #ManyToOne is required. Where you set #ManyToOne - its related entity will have "relation id" and foreign key.
Refer to the TypeORM documentation for more details about this, you will also find an example of this type of relation.
#Entity()
export class User {
#OneToMany(type => Post, post => post.user)
posts: Post[];
}
#Entity()
export class Post {
#PrimaryGeneratedColumn()
id: number;
#Column()
user_id: number;
#ManyToOne(type => User)
#JoinColumn({name: 'user_id', referencedColumnName: 'id'})
user: User;
#OneToMany(type => Image, image => image.post)
images: Image[];
}
#Entity()
export class Image {
#PrimaryGeneratedColumn()
id: number;
#Column()
post_id: number;
#ManyToOne(type => Post)
#JoinColumn({name : 'post_id', referencedColumnName: 'id'})
post: Post;
}
Try this