node.jsgraphqlnestjsgraphql-js

How to create multilevel nested queries in nestjs/graphql using @ResolveField?


Hello. I can't figure out how to create multiple levels of nested queries with @ResolveFiled. I hope for your help. 🙏

What I'm doing and Context:

I have a product. The product has a supplier. A vendor-specific product contains product variants. Variants contain options.

I need to make a request in 4 levels:

  1. Product
  2. ProductHasProvider
  3. Product Variants
  4. Variant Options

I use the "Code First" approach, created an ObjectType for each entity. Next, I create a "Product" resolver.

Creating a second level "ProductHasProvider" with @ResolveField

When adding a ResolveField ("Providers") - it appears inside the main resolver "Product" and resolves the Providers ObjectType. Okay, it works, I can make requests at the 2nd level correctly.

@ResolveField('Providers', () => [ProductHasProvider])
async getProductProviders(@Parent() product: Product) {
  const { id } = product;
  return await this.productListService.ProductsProviders({ id });
}
  1. I want to make third level where ProductHasProvider has Variants. I decorate ProductHasProvider as the parent.
@ResolveField('variants', (type) => [Variant])
async getVariants(@Parent() productHasProvider: ProductHasProvider) {
  const { id } = productHasProvider;
  return await this.productListService.getVariants({ id });
}

In this case, this ResolveField defines the ObjectType for [Variants], but for some reason at the first level. That is, in Apollo studio, the field is displayed in "Product". I can't query Variants for ProductHasProvider.

query Products {
  getProducts {
    Providers {
      id
    }
    variants {
      id
      options {
        id
      }
    }
  }
}

Expected behavior:

I add a new @ResolveField(() => [Variants]) with "ProductHasProvider" parent (Which is already @ResorveField for Product). And I can do 3rd and 4th level queries.

query Products {
  getProducts {
    id
    Providers {
      id
      variants {
        id
        options {
          id
        }
      }
    }
  }
}

Please tell me what I'm doing wrong and how to achieve what I want. Thank you.🙏


Solution

  • @ResolveField is to be put in a Resolver, to specify how to return a specific field for a specific entity.

    In your case, you have a Resolver for Products, in which you specify a @ResolveField for the field Providers.

    I'm guessing that you are adding another @ResolveField in the same Resolver, and it will specify how to return another field of Products.

    What you want is to create another Resolver, for Providers, in which you specify how to return the field variants.

    Here is how it is working for me :

    @Resolver('Product')
    export class ProductsResolver {
    
      @ResolveField('Providers', () => [ProductHasProvider])
      async getProductProviders (@Parent() product: Product) {
        const { id } = product;
        return await this.productListService.ProductsProviders( { id });
      }
    }
    
    @Resolver('Provider')
    export class ProvidersResolver {
    
      @ResolveField('variants', () => [Variant])
      async getProductProviders (@Parent() provider: ProductHasProvider) {
        const { id } = provider;
        return await this.variantsService.getVariantForProvider( { id });
      }
    }