Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get requested fields inside GraphQL resolver?

I am using graphql-tools. After receiving a GraphQL query, I execute a search using ElasticSearch and return the data.

However, usually the requested query includes only a few of the possible fields, not all. I want to pass only the requested fields to ElasticSearch. First, I need to get the requested fields.

I can already get the whole query as a string. For example, in the resolver,

const resolvers = {
  Query: {
    async user(p, args, context) {
      //can print  query as following
      console.log(context.query)                
    }
    .....
  }
}

It prints as

query User { user(id:"111") { id  name address } }

Is there any way to get the requested fields in a format like

{ id:"",  name:"", address:"" }
like image 725
arslan Avatar asked Dec 28 '17 09:12

arslan


People also ask

How does resolver work in GraphQL?

Each field on each type is backed by a function called the resolver which is provided by the GraphQL server developer. When a field is executed, the corresponding resolver is called to produce the next value. If a field produces a scalar value like a string or number, then the execution completes.

What is info in GraphQL resolver?

info contains the query AST and more execution information So, there's no other way than digging into the code. On a very high-level, it can be stated that the info object contains the AST of the incoming GraphQL query. Thanks to that, the resolvers know which fields they need to return.

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).


3 Answers

In graphql-js resolvers expose a fourth argument called resolve info. This field contains more information about the field.

From the GraphQL docs GraphQLObjectType config parameter type definition:

// See below about resolver functions.
type GraphQLFieldResolveFn = (
  source?: any,
  args?: {[argName: string]: any},
  context?: any,
  info?: GraphQLResolveInfo
) => any

type GraphQLResolveInfo = {
  fieldName: string,
  fieldNodes: Array<Field>,
  returnType: GraphQLOutputType,
  parentType: GraphQLCompositeType,
  schema: GraphQLSchema,
  fragments: { [fragmentName: string]: FragmentDefinition },
  rootValue: any,
  operation: OperationDefinition,
  variableValues: { [variableName: string]: any },
}

In the fieldNodes field you can search for your field and get the selectionSet for the particular field. From here it gets tricky since the selections can be normal field selections, fragments or inline fragments. You would have to merge all of them to know all fields that are selected on a field.

like image 118
Herku Avatar answered Oct 10 '22 16:10

Herku


There is an info object passed as the 4th argument in the resolver. This argument contains the information you're looking for.

It can be helpful to use a library as graphql-fields to help you parse the graphql query data:

const graphqlFields = require('graphql-fields');

const resolvers = {
  Query: {
    async user(_, args, context, info) {
      const topLevelFields = graphqlFields(info);
      console.log(Object.keys(topLevelFields)); // ['id', 'name', 'address']
    },
};
like image 20
s.meijer Avatar answered Oct 10 '22 18:10

s.meijer


Similarly for graphql-java you may do the same by extending the field parameters with myGetUsersResolverMethod(... DataFetchingEnvironment env).

  • This DataFetchingEnvironment would be injected for you and you can traverse through this DataFetchingEnvironment object for any part of the graph/query.

  • This Object allows you to know more about what is being fetched and what arguments have been provided.

Example:

    public List<User> getUsers(final UsersFilter filter, DataFetchingEnvironment env) {

    DataFetchingFieldSelectionSet selectionSet = env.getSelectionSet();
    selectionSet.getFields(); // <---List of selected fields
    selectionSet.getArguments(); // <--- Similarly but MAP
    ...

    }

In fact you may be alluding to look ahead data fetching. The above should give you enough insights into the fields requested and you can take it from there to tailor you downstream calls manually. But also you may look into a more efficient way to do this by using the data fetchers for Building efficient data fetchers by looking ahead

like image 2
DaddyMoe Avatar answered Oct 10 '22 16:10

DaddyMoe