graphqlgraphql-js

GraphQL using nested query arguments on parent or parent arguments on nested query


I have a product and items

Product:

{
  id: Int
  style_id: Int
  items: [items]
}

Items:

{
  id: Int
  product_id: Int
  size: String
}

I want to query products but only get back products that have an item with a size.

So a query could look like this:

products(size: ["S","M"]) {
  id
  style_id
  items(size: ["S","M"]) {
    id
    size
  }
}

But it seems like there should be a way where I can just do

products {
  id
  style_id
  items(size: ["S","M"]) {
    id
    size
  }
}

And in the resolver for the products I can grab arguments from the nested query and use them. In this case add the check to only return products that have those sizes. This way I have the top level returned with pagination correct instead of a lot of empty products.

Is this possible or atleast doing it the other way around:

products(size: ["S","M"]) {
  id
  style_id
  items {
    id
    size
  }
}

And sending the size argument down to the items resolver? Only way I know would be through context but the one place I found this they said that it is not a great idea because context spans the full query in all depths.


Solution

  • I agree with @DenisCappelini's answer. If possible, you can create a new type which represents only Products that have an Item.

    However, if you don't want to do that, or if you're just interested in general about how a top-level selector can know about arguments on child selectors, here is a way to do that:

    There are 2 ways to do it.


    To do this:

    products {
      id
      style_id
      items(size: ["S","M"]) {
        id
        size
      }
    }
    

    In graphql, resolvers have this signature:

    (obj, args, context, info) => {}
    

    The 4th argument, info, contains information about the entire request. Namely, it knows about arguments on the child selectors.

    Use this package, or a similar one because there are others, to parse info for you: https://www.npmjs.com/package/graphql-parse-resolve-info


    The above is quite a lot of work, so if you want to do this instead:

    products(size: ["S","M"]) {
      id
      style_id
      items {
        id
        size
      }
    }
    

    Then in your resolver for products, you need to also return size. Suppose this is your resolver for products:

    (parent, args) => {
      ...
      return {
        id: '',
        style_id: ''
      }
    }
    

    Modify your resolver to also return size like this:

    (parent, args) => {
      ...
      return {
        id: '',
        style_id: '',
        size: ["S", "M"]
      }
    }
    

    Now in your resolve for products.items, you will have access to the size, like this:

    (product, args) => {
      const size = product.size
    }