[ ] Regression
[ ] Bug report
[x] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
No possibility to create nested resolvers (or no info in the docs).
Possibility to make nested resolvers (and/or appropriate info in the docs).
In the docs we have example how to make Posts property resolver on Author Resolver. But we don't have any info, how to make nested resolver on those posts. For example - we'd like to lazy fetch (only when someone requests the field) posts statistics. Seems like this isn't possible at the moment?
@Resolver(of => Author)
export class AuthorResolver {
constructor(
private readonly authorsService: AuthorsService,
private readonly postsService: PostsService,
private readonly statsService: StatsService, // hypothetical, low-performing service
) {}
@Query(returns => Author)
async author(@Args({ name: 'id', type: () => Int }) id: number) {
return await this.authorsService.findOneById(id);
}
@ResolveProperty()
async posts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
// This does NOT work. Nor is mentioned anywhere in the docs.
@ResolveProperty()
async stats(@Parent() post) {
const { id } = post;
return await this.statsService.getPostStatistics({ postId: id });
}
}
So, is this possible in any way? If not, would a feature request like this be acceptable?
One of the biggest advantages of graphQL is lazy fetching nested fields (graph-like connections). Current implementation of graphQL decorators probably doesn't allow creating nested resolvers.
Edit: Found similar question here: #162, but no luck with an answer yet. :(
I'm not sure I completely follow, but how would it know where the @Parent post is from? Why can't this be in its own Resolver class? It looks like you're trying to shove two entities in the same Resolver, when they should really be two separate classes
@krislefeber I just want to make possible query like this one:
{
Level1(id: "someId") {
level1Field
Level2(optionalParam: "optional") {
level2Field
Level3(optionalParam: "optional") {
level3Field
}
}
}
}
It is perfectly fine, to have it in separate resolver classes... but is it possible to nest it like this?
You would end up with something like this.
First define the classes. Notice that there are no decorators on the object references. We instead add the mappings to that object in the Resolver.
@ObjectType()
class Level1 {
@Field(() => ID)
level1Field: string;
level2: Promise<Level2>;
}
@ObjectType()
class Level2 {
@Field(() => ID)
level2Field: string;
level3: Promise<Level3>;
}
@ObjectType()
class Level3 {
@Field(() => ID)
level3Field: string;
}
In the first resolver, add the initial query for a search by id. We also add the getLevel2Property function, which tells graphql how to connect the two objects.
@Resolver(Level1)
class Level1Resolver {
@Query(() => Level1, {
name: 'Level1',
})
public async getLevel1(@Args('id') id: string): Promise<Level1> {
const level1 = new Level1();
level1.level1Field = id;
return level1;
}
@ResolveProperty(() => Level2, {
name: 'Level2',
})
public async getLevel2Property(@Args('optionalParam') optionalParam: string, @Parent() parent: Level1): Promise<Level2> {
// this can be whatever logic you have to connect the two entities
const level2 = new Level2();
if (optionalParam) {
level2.level2Field = optionalParam;
}
return level2;
}
}
In the level 2 resolver, we need to add the mapping to Level3. Once again, the code inside getLevel3Property can be whatever you want.
@Resolver(Level2)
class Level2Resolver {
@ResolveProperty(() => Level3, {
name: 'Level3',
})
public async getLevel3Property(@Args('optionalParam') optionalParam: string, @Parent() parent: Level2) {
const level3 = new Level3();
if (optionalParam) {
level3.level3Field = optionalParam;
}
return level3;
}
}
Let me know if that makes sense. Thanks
@krislefeber Thanks for help, that did work!
I actually tried similar approach before but had some errors, so I thought it won't work like that. Turns out it does, so I must have messed something up. 馃
Anyway - thanks again, I appreciate it. 馃檪
How to get Level1 input fields like (id: "someId") in Level2 resolver?
{
Level1(id: "someId") {
level1Field
Level2 {
level2Field
Level3 {
level3Field
}
}
}
}
@m7rlin, couldn't you just pass that input in again?
@krislefeber if I pass input again like so:
{
Level1(id: "someId") {
level1Field
Level2(id: "someId") {
level2Field
Level3 {
level3Field
}
}
}
}
it will work but I don't want duplication and maybe later on I will add another input at Level2.
I created something like this:
@Resolver(Level1)
class Level1Resolver {
...
@ResolveProperty(() => Level2, {
name: 'Level2',
})
public async getLevel2Property(@Args('input') input: string, @Parent() parent: Level1) {
return { _inputLevel1: input};
}
}
On Level2 resolver I can access parent object. I don't know any other solution. It will be nice to have custom decorator which will achieve similar solution.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
You would end up with something like this.
First define the classes. Notice that there are no decorators on the object references. We instead add the mappings to that object in the Resolver.
In the first resolver, add the initial query for a search by id. We also add the getLevel2Property function, which tells graphql how to connect the two objects.
In the level 2 resolver, we need to add the mapping to Level3. Once again, the code inside getLevel3Property can be whatever you want.
Let me know if that makes sense. Thanks