I am using typeorm to manage my postgresql database in nestjs. I had to save a json response in the db, so I saved whole response under one column. This is my entity.
/* eslint-disable prettier/prettier */
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
#Entity({name:'travel_bookings'})
export class TravelBookings {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column('uuid')
userId:string;
#Column({ type: 'json' })
booking_response: string;
#Column({ nullable: true })
Status:string;
#CreateDateColumn({ name: 'created_at' }) 'created_at': Date;
#UpdateDateColumn({ name: 'updated_at' }) 'updated_at': Date;
}
`
The booking_response is a long json data, in which there is one id. Now I am creating a function to update Status where the id inside the json matches with the id I provide.
This is one example json response -
{"type":"flight-order","id":"eJzTd9f397V09w8FAAs4AmY%3D","queuingOfficeId":"NCE4D31SB","associatedRecords":[{"reference":"OM9GOU","creationDate":"2023-02-07T11:40:00.000","originSystemCode":"GDS","flightOfferId":"1"}],"flightOffers":[{"type":"flight-offer","id":"1","source":"GDS","nonHomogeneous":false,"lastTicketingDate":"2023-02-08","itineraries":[{"segments":[{"departure":{"iataCode":"BOS","terminal":"C","at":"2023-03-10T22:55:00"},"arrival":{"iataCode":"LIS","terminal":"1","at":"2023-03-11T10:20:00"},"carrierCode":"TP","number":"216","aircraft":{"code":"32Q"},"duration":"PT6H25M","id":"9","numberOfStops":0,"co2Emissions":[{"weight":303,"weightUnit":"KG","cabin":"ECONOMY"}]},{"departure":{"iataCode":"LIS","terminal":"1","at":"2023-03-11T11:45:00"},"arrival":{"iataCode":"MAD","terminal":"2","at":"2023-03-11T14:05:00"},"carrierCode":"TP","number":"1014","aircraft":{"code":"32Q"},"duration":"PT1H20M","id":"10","numberOfStops":0,"co2Emissions":[{"weight":68,"weightUnit":"KG","cabin":"ECONOMY"}]}]},{"segments":[{"departure":{"iataCode":"MAD","terminal":"2","at":"2023-03-11T21:10:00"},"arrival":{"iataCode":"LIS","terminal":"1","at":"2023-03-11T21:30:00"},"carrierCode":"TP","number":"1019","aircraft":{"code":"321"},"duration":"PT1H20M","id":"83","numberOfStops":0,"co2Emissions":[{"weight":68,"weightUnit":"KG","cabin":"ECONOMY"}]},{"departure":{"iataCode":"LIS","terminal":"1","at":"2023-03-12T11:40:00"},"arrival":{"iataCode":"BOS","terminal":"E","at":"2023-03-12T15:20:00"},"carrierCode":"TP","number":"217","aircraft":{"code":"32Q"},"duration":"PT7H40M","id":"84","numberOfStops":0,"co2Emissions":[{"weight":303,"weightUnit":"KG","cabin":"ECONOMY"}]}]}],"price":{"currency":"USD","total":"613.85","base":"184.00","fees":[{"amount":"0.00","type":"TICKETING"},{"amount":"0.00","type":"SUPPLIER"},{"amount":"0.00","type":"FORM_OF_PAYMENT"}],"grandTotal":"613.85","billingCurrency":"USD"},"pricingOptions":{"fareType":["PUBLISHED"],"includedCheckedBagsOnly":false},"validatingAirlineCodes":["TP"],"travelerPricings":[{"travelerId":"1","fareOption":"STANDARD","travelerType":"ADULT","price":{"currency":"USD","total":"613.85","base":"184.00","taxes":[{"amount":"5.60","code":"AY"},{"amount":"4.40","code":"J9"},{"amount":"15.80","code":"JD"},{"amount":"0.70","code":"OG"},{"amount":"16.10","code":"PT"},{"amount":"3.60","code":"QV"},{"amount":"42.20","code":"US"},{"amount":"3.83","code":"XA"},{"amount":"4.50","code":"XF"},{"amount":"7.00","code":"XY"},{"amount":"6.52","code":"YC"},{"amount":"27.60","code":"YP"},{"amount":"292.00","code":"YQ"}],"refundableTaxes":"86.75"},"fareDetailsBySegment":[{"segmentId":"9","cabin":"ECONOMY","fareBasis":"UUSDSI0E","brandedFare":"DISCOUNT","class":"U","includedCheckedBags":{"quantity":0}},{"segmentId":"10","cabin":"ECONOMY","fareBasis":"UUSDSI0E","brandedFare":"DISCOUNT","class":"U","includedCheckedBags":{"quantity":0}},{"segmentId":"83","cabin":"ECONOMY","fareBasis":"UUSDSI0E","brandedFare":"DISCOUNT","class":"U","includedCheckedBags":{"quantity":0}},{"segmentId":"84","cabin":"ECONOMY","fareBasis":"UUSDSI0E","brandedFare":"DISCOUNT","class":"U","includedCheckedBags":{"quantity":0}}]}]}],"travelers":[{"id":"1","dateOfBirth":"1982-01-16","gender":"MALE","name":{"firstName":"JORGE","lastName":"GONZALES"},"documents":[{"number":"00000000","issuanceDate":"2015-04-14","expiryDate":"2025-04-14","issuanceCountry":"ES","issuanceLocation":"Madrid","nationality":"ES","birthPlace":"Madrid","documentType":"PASSPORT","holder":true}],"contact":{"purpose":"STANDARD","phones":[{"deviceType":"MOBILE","countryCallingCode":"34","number":"480080076"}],"emailAddress":"jorge.gonzales833#telefonica.es"}}],"remarks":{"general":[{"subType":"GENERAL_MISCELLANEOUS","text":"ONLINE BOOKING FROM INCREIBLE VIAJES"}]},"ticketingAgreement":{"option":"DELAY_TO_CANCEL","delay":"6D"},"automatedProcess":[{"code":"IMMEDIATE","queue":{"number":"0","category":"0"},"officeId":"NCE4D31SB"}],"contacts":[{"addresseeName":{"firstName":"PABLO RODRIGUEZ"},"address":{"lines":["Calle Prado, 16"],"postalCode":"28014","countryCode":"ES","cityName":"Madrid"},"purpose":"STANDARD","phones":[{"deviceType":"LANDLINE","countryCallingCode":"34","number":"480080071"},{"deviceType":"MOBILE","countryCallingCode":"33","number":"480080072"}],"companyName":"INCREIBLE VIAJES","emailAddress":"support#increibleviajes.es"}]}
This is my function -
async flightCancel(data) {
var amadeus = await new Amadeus({
clientId: process.env.API_KEY,
clientSecret: process.env.API_SECRET
});
// const output=await this.travelBookingsRepo.findOne({where:{ booking_response:"eJzTd9f397V09w8FAAs4AmY%3D"}})
// console.log(output);
this.travelBookingsRepo.update({ booking_response: { id: data } },{Status:'Cancelled'})
return amadeus.booking.flightOrder(data).delete();
}
I found some solutions on web using entityManager , getManager/getConnection but these are deprecated and work no more. If issue is about update function, then I have tried it with findOne and save methods too but still unsuccessful. Please help me in resolving my issue.
If you are using after typeorm#0.3.0, you can not use entityManager , getManager/getConnection.
Instead you could use 'DataSource'
You could check my github.
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 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[]
}
I am trying to create simple appliaction with Nest.js, GraphQL and MongoDB. I wnated to use TypeORM and TypeGraphql to generate my schema and make a connection with localhost databasebut but i can not run my server with nest start becouse I am getting this error:
UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL output type for getArticles
I have no idea why i am getting this error. My class ArticleEntity does't has any not primary types, so there should not be any problem. I tried to remove () => ID from #Field() decorator of filed _id of ArticleEntity class but it didn't helped
ArticleResolver
#Resolver(() => ArticleEntity)
export class ArticlesResolver {
constructor(
private readonly articlesService: ArticlesService) {}
#Query(() => String)
async hello(): Promise<string> {
return 'Hello world';
}
#Query(() => [ArticleEntity])
async getArticles(): Promise<ArticleEntity[]> {
return await this.articlesService.findAll();
}
}
ArticleService
#Injectable()
export class ArticlesService {
constructor(
#InjectRepository(ArticleEntity)
private readonly articleRepository: MongoRepository<ArticleEntity>,
) {}
async findAll(): Promise<ArticleEntity[]> {
return await this.articleRepository.find();
}
}
ArticleEntity
#Entity()
export class ArticleEntity {
#Field(() => ID)
#ObjectIdColumn()
_id: string;
#Field()
#Column()
title: string;
#Field()
#Column()
description: string;
}
ArticleDTO
#InputType()
export class CreateArticleDTO {
#Field()
readonly title: string;
#Field()
readonly description: string;
}
If you need anything else comment
ArticleEntity should be decorated with the #ObjectType decorator as shown in the docs https://typegraphql.com/docs/types-and-fields.html.
#Entity()
#ObjectType()
export class ArticleEntity {
...
}
For anyone who gets this error and uses enums, you may be missing a call to registerEnumType.
In my case, I was using the #ObjectType decorator, but I was importing from type-graphql. I imported from #nestjs/graphql instead, and the problem was resolved.
import { ObjectType } from '#nestjs/graphql';
See here for a related discussion on GitHub.
I was using MongoDB and I had my Query return the schema instead of the model class.
Changing #Query((returns) => UserSchema) to #Query((returns) => User) fixed the issue for me.
user.schema.ts
#ObjectType()
#Schema({ versionKey: `version` })
export class User {
#Field()
_id: string
#Prop({ required: true })
#Field()
email: string
#Prop({ required: true })
password: string
}
export const UserSchema = SchemaFactory.createForClass(User)
user.resolver.ts
#Query((returns) => User)
async user(): Promise<UserDocument> {
const newUser = new this.userModel({
id: ``,
email: `test#test.com`,
password: `abcdefg`,
})
return await newUser.save()
}
Output model class should be decorated with #ObjectType() and then all properties of that class will decorated with #Field().
For any one who is using a custom output model class and NOT an entity(sequelize, typeorm, prisma etc). Because I was using database entity first, everything was working fine till I moved to a more customized output model.
One more case would be someone using class A as output and class B is used within A
export class A{
id: number;
name:string;
childProperty: B
. . . . .
}
export class B{
prop1:string;
prop2:string;
}
In that case class B should also be decorated with #ObjectType and fields (prop1 , prop2 ) should be also be decorated with #Field as well.
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?