I'm trying to figure out the best way (or best-practice way) to resolve the lazy relationships in my Nestjs TypeORM postgresql app.
I've defined some OneToMany and ManyToOne relationships in the entities as lazy, and when querying for an array of one of these entities, the best solution I have found so far is a huge mess of promise rosolving.
My Customer
entity (customer.entity.ts):
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne, OneToMany } from 'typeorm';
import { IsEmail } from 'class-validator';
import { ReservationDate } from '../reservation_date/reservation_date.entity';
@Entity()
export class Customer {
@PrimaryGeneratedColumn('uuid')
id: string;
@Index()
@Column()
@IsEmail()
email: string;
@Column()
firstName: string;
@Column()
lastName: string;
@CreateDateColumn()
createDate: Date;
@UpdateDateColumn()
updateDate: Date;
@OneToMany(type => ReservationDate, reservationDate => reservationDate.customer)
reservationDates: Promise<ReservationDate[]>;
}
and my ReservationDate
entity (reservation_date.entity.ts):
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne } from 'typeorm';
import { Customer } from '../customer/customer.entity';
@Entity()
export class ReservationDate {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
date: Date;
@CreateDateColumn()
createDate: Date;
@UpdateDateColumn()
updateDate: Date;
@ManyToOne(type => Customer, customer => customer.reservationDates, { onDelete: 'CASCADE' })
customer: Promise<Customer>;
}
I need to retrieve an array of ReservationDate
s, and send that off somewhere, but I need it to resolve the customer
field (eg eager-load that relationship). This is what "works":
const reservationDates: ReservationDate[] = await this.reservationDateRepository
.createQueryBuilder('reservationDate')
.leftJoinAndSelect('reservationDate.customer', 'customer')
.getMany();
// Same result as query builder above
// const reservationDates: ReservationDate[] = await this.reservationDateRepository
// .find({
// relations: ['customer'],
// take: 5
// });
console.log(reservationDates[0].car); // => Promise { <pending> }
console.log(reservationDates[0].__car__); // => Car { id: string, owner... }
// this is where it gets ugly
const reservationDatesWithResolvedRelationships = await Promise.all(
reservationDates.map(async (resDate: ReservationDate) => {
const withResolved: ReservationDateRepoObject = { ...resDate };
withResolved.customer = await resDate.customer;
return withResolved;
})
);
console.log(reservationDatesWithResolvedRelationships[0].car); // => Car { id: string, owner... }
// send off the JSON-like object here
I feel like there should be a way to do the joins and force an eager load, but neither the query builder or repository.find
methods seem to do this with the way I have it set up.
I've also tried adding { lazy: true }
and { eager: true }
to the relationships in the entities, but nothing changed.
EDIT:
Still not much luck yet, however I was able to hack together a pseudo-solution.
private parseRaw = name => raw => {
const parsed = new ReservationDate();
Object.entries(raw).forEach(([typeKey, value]) => {
const [type, key] = typeKey.split('_');
if (type === name) {
parsed[key] = value;
} else {
parsed[type] = parsed[type] || {};
parsed[type][key] = value;
}
});
return parsed;
};
let reservationDates: ReservationDate[] = (await this.reservationDateRepository
.createQueryBuilder('reservationDate')
.leftJoinAndSelect('reservationDate.customer', 'customer')
.limit(5)
.getRawMany()).map(this.parseRaw('reservationDate'));
Super ugly hack, but it converts the raw, snake_case
db results to the camelCase
json object I need to send...