Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access relationship ID from Parent's joined field in NestJS/TypeORM

I'm setting up a NestJS GraphQL API that utilizes TypeORM, and am having trouble implementing relationships between entities.

Specifically, the TypeORM relationships are working great, and the entities are linking correctly in the database. However, the problem comes in when I try to query the API to get the results.

Right now I have 2 entities, each with their own resolver: Users and Photos. Each User can have multiple Photos, while each Photo is only connected to one User (Many-to-One).

Here's how the entities are linked with TypeORM

Photo Entity, with a relationship to the User Entity

@ManyToOne(type => User, user => user.photos, {
    onDelete: 'CASCADE',
})
@JoinColumn()
user: User;

User Entity, completing connection to the Photo Entity

@OneToMany(type => Photo, photo => photo.user, {
    eager: true,
})
photos: Photo[];

This code works, and let's us retrieve a User's Photos

User resolver

@ResolveProperty('photos')
async photos(@Parent() user): Promise<Photo[]> {
    return await this.service.readPhotos(user.id);
}

User service

async readPhotos(userId): Promise<Photo[]> {
    return await this.photoRepository.find({
        where: {
            user: userId
        }
    });
}

* Note that the photoRepository is able to be filtered by the 'user' field. *

This code, however, does not work. It should let us view which User is connected to the Photo, instead it returns null.

Photo resolver

@ResolveProperty('user')
async user(@Parent() photo): Promise<User> {
    console.log(photo);
    return await this.service.readUser(photo.user);
}

This Photo resolver seems to contain the problem; the photo object being output by the console indicates that while the @Parent photo object has all of its static fields available (like the ID, datePublished, URL), for some reason the actual 'user' field is not accessible here. So the 'photo.user' variable is null. * Note that this seems to indicated that the photoRepository is UNABLE to be filtered by/access the 'user' field. *

Photo service

async readUser(userId): Promise<User> {
    return await this.userRepository.findOne({
        where: {
            user: userId
        }
    });
}

This returns null since the userId is blank, due to the previous Photo resolver not being able to access the 'user' field.

Conclusion

Why can't the Photo resolver access the @Parent photo 'user' field? The User service seems to be able to filter by the 'user' field just fine, yet I can't seem to be able to access the Photo 'user' field directly.

Thank you for any help on this! I've been stumped on this for the last two days...

like image 272
Turgon Avatar asked Dec 23 '22 01:12

Turgon


2 Answers

So you'd like to access the user huh? photo.user? In FindOptions there's a key called 'relations' this will fetch and apply that for you. So in your example

async readPhotos(userId): Promise<Photo[]> {
    return await this.photoRepository.find({
        where: {
            user: userId
        },
        relations: ['user'],
    });
}

This should now be photo.user. Another way would be the reverse

async readUser(userId): Promise<User> {
    return await this.userRepository.findOne({
        where: {
            user: userId
        },
        relations: ['photos'],
    });
}

This should now return user.photos as an array of Photos

You can find more examples of FindOptions usages here http://typeorm.io/#/find-options

Hope this helps!

like image 144
bashleigh Avatar answered Dec 31 '22 08:12

bashleigh


I was struggling with a similar problem and although @bashleigh's solution works if you want the entire entity returned I only needed the id. So if that's your case you can pass the loadRelationIds option, and set it to true.

return await this.photoRepository.find({
 where: {
  id: photoId
 },
 loadRelationIds: true
});

This will return user as just the id (string or int).

like image 23
AndreVitorio Avatar answered Dec 31 '22 09:12

AndreVitorio