Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graphql-Access arguments in child resolvers

I am using apollo-server and apollo-graphql-tools and I have following schema

type TotalVehicleResponse {
  totalCars: Int
  totalTrucks: Int
}

type RootQuery {
  getTotalVehicals(color: String): TotalVehicleResponse
}

schema {
  query: RootQuery
}

and Resolver functions are like this

{
  RootQuery: {
    getTotalVehicals: async (root, args, context) => {
      // args = {color: 'something'}
      return {};
    },
    TotalVehicleResponse: {
      totalCars: async (root, args, conext) => {
        // args is empty({}) here
        .........
        .........
      },
      totalTrucks: async (root, args, conext) => {
        // args is empty({}) here
        .........
        .........
      }
    }
  }
}

My question is that how can I access args which is available in root resolver(getTotalVehicals) in any of the child resolvers?

like image 538
Manan Vaghasiya Avatar asked Jan 22 '18 13:01

Manan Vaghasiya


People also ask

What is the second argument passed into GraphQL resolvers used for?

To access this id , we can use the second parameter in the resolver: args . args is an object that contains all GraphQL arguments that were provided for the field. We can destructure this object to access the id property, then pass that id into our getTrack method. Then, we can return the results of that method.

How do you access the context in GraphQL?

The Context can be accessed directly in resolve function or within Dependency Injection using CONTEXT token.

How do you test a resolver GraphQL?

To start testing GraphQL queries, use the easygraphql-tester library. The library can be used to test all kinds of resolvers, queries, mutations, and subscriptions, but start by testing the getBooks query. First, create a tester object from your schema.


3 Answers

args refer strictly to the arguments provided in the query to that field. If you want values to be made available to child resolvers, you can simply return them from the parent resolver.

{
  RootQuery: {
    getTotalVehicles: async (root, args, context) => {
      return { color: args.color };
    },
    TotalVehicleResponse: {
      totalCars: async (root, args, context) => {
        // root contains color here
      },
      totalTrucks: async (root, args, context) => {
        // root contains color here
      }
    }
  }
}
like image 126
imranolas Avatar answered Oct 03 '22 01:10

imranolas


If you know you are using variables there is another way, other than the accepted answer, using the fourth argument of the resolver function: info.

This info argument contains a field variableValues amongst other fields. This field doesn't strictly contain the parent's args, but if your operation is executed with variables that are passed to the parent resolver, then you'll have access to them via the info.variableValues from all the relevant resolver functions.

So if your operation is called like this for example:

query GetTotalVehicalsOperation($color: String) {
  getTotalVehicals(color: $color) {
    totalCars
    totalTrucks   
  }
}

... with variables: {color: 'something'}

you'll have access to the variables from the other resolvers:

{
  RootQuery: {
    getTotalVehicles: async (root, args, context, info) => {
      //info.variableValues contains {color: 'something'}          
      return {};
    },
    TotalVehicleResponse: {
      totalCars: async (root, args, context, info) => {
        //same here: info.variableValues contains {color: 'something'}
      },
      totalTrucks: async (root, args, context, info) => {
        //and also here: info.variableValues contains {color: 'something'}
      }
    }
  }
}
like image 20
Tal Z Avatar answered Oct 03 '22 00:10

Tal Z


TLDR: Add your arguments to the field

(Client Side) change from:

Car(type: $type, materialType: $materialType){
  id
  material
  name
  ...
}

(Client Side) To:

Car(type: $type){
  id,
  material(materialType: $materialType) // moved here
  name
  ...
}

Then, access your argument in your server fieldResolver (material field in this case).

Longer version

Try not to pass your argument through root, except IDs, arguments that is not from client or a parent object, anything else use field level argument (unless you have a very good reason not to)

Why?

There are a few reasons:

  1. Tight Coupling

    it leads to coupling and it's very hard to scale up schemas -From @Bruno Ribeiro in the comment section:

  2. Difficult to troubleshoot

    One level is still fine, but when someone in your company found a way to pass the argument down deep through the roots, it is difficult to debug how it is missing.

  3. Leaking unnecessary information to children

    Passing arguments through root also means passing to every other child, desired one or not.

  4. Mixing up parent object and argument

    Your parent object might have the same property key as argument, eg: offset, by supplying another offset, you will likely got an undesirable result.

How?

A simple query can grow from this:

[Root] Car(
  color:white, 
  type:sedan, 
  seat:leather
) {
  id,
  seat,
  numberOfPassengers,
  ...
}

To this:

[Root] Car(
  color:white, 
  type:sedan, 
  seat:leather, 
  seatColor:black,
  rimColor: blue,
  rimShape: OutOfTheWorld,
  ...
) {
  id,
  seat,
  numberOfPassengers,
  ...
}

Instead of passing the argument around, you can do this

[Root] Car(
  color:white, 
  type:sedan
  ...
) {
  id
  seat(type:leather, color:black),
  rim(color: blue, shape: OutOfTheWorld){
    // nested query
    material(hardness: high), // solved `Why no.2`: deep argument. 
    
    // More nested
    brand(trustWorthy: high) {
      priceRange(range: mid),
      area,
      ...
    },
    id
  }
  numberOfPassengers,
  ...
}

instead of clumping all arguments into one root, now each field is responsible for its argument and resolver.

When to apply?

Whenever you find yourself creating a dedicated resolver for that field, pass the argument to the field (not root, and worse: info)

like image 26
cYee Avatar answered Oct 03 '22 00:10

cYee