Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 721
BrinkDaDrink Avatar asked Dec 07 '18 08:12

BrinkDaDrink


People also ask

How do you query nested objects in GraphQL?

When setting up a field whose value is a custom type, we have to define a function that tells GraphQL how to get that custom type. In our case, we want to tell GraphQL how to get the posts if we have the author. We do that by defining a new root property inside resolvers.

How do you pass an argument in GraphQL query?

When you're passing arguments in code, it's generally better to avoid constructing the whole query string yourself. Instead, you can use $ syntax to define variables in your query, and pass the variables as a separate map. . then(data => console.

What is __ Typename in GraphQL?

The __typename field returns the object type's name as a String (e.g., Book or Author ). GraphQL clients use an object's __typename for many purposes, such as to determine which type was returned by a field that can return multiple types (i.e., a union or interface).

What are the three types of operations in GraphQL?

There are three types of operations that GraphQL models: query – a read‐only fetch. mutation – a write followed by a fetch. subscription – a long‐lived request that fetches data in response to source events.


3 Answers

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
}
like image 59
Croolsby Avatar answered Oct 09 '22 12:10

Croolsby


IMO you should have a ProductFilterInputType which is represented by a GraphQLList(GraphQLString), and this resolver filters the products based on this list.

import { GraphQLList, GraphQLString } from 'graphql';

const ProductFilterInputType = new GraphQLInputObjectType({
  name: 'ProductFilter',
  fields: () => ({
    size: {
      type: GraphQLList(GraphQLString),
      description: 'list of sizes',
    }
  }),
});

Hope it helps :)

like image 1
Denis Cappelini Avatar answered Oct 09 '22 10:10

Denis Cappelini


I found this useful #reference

//the typedef:

type Post {
    _id: String
    title: String
    private: Boolean
    author(username: String): Author
}
//the resolver:
Post: {
        author(post, {username}){
        //response
      },
    }
// usage
{
    posts(private: true){
    _id,
    title,
    author(username: "theara"){
      _id,
      username
    }
  }
}
like image 1
Tun Cham Roeun Avatar answered Oct 09 '22 12:10

Tun Cham Roeun