GraphQL: Understanding Datafetchers and it’s use

Knoldus Blog Audio
Reading Time: 3 minutes

The world is a stage where all of us are artists and constant learning is the foundation of success. To boost up your knowledge pot with various technology, here comes another blog about a developing technology GraphQL.
GraphQL is a query language introduced by Facebook. To get in basic detail you can visit previous blogs. Here we will discuss DataFetchers.
Also, if you want to start the basics and explore GraphQL from scratch, please refer to my previous blogs Beginners guide to Graphql with Springboot and Getting started with Graphql.

DataFetchers

Data fetchers are also known as “Resolvers” in many graphql implementations”. Resolver function is the place where graphql resolves the type or the field and gets its value from the configured resources like a database or other APIs or from cache etc and returns data back to user/caller.

How graphql fetches data

Each field in graphql has been associated with graphql.schema.DataFetcher. Some fields will use specialized data fetcher code that knows how to go to a database say to get field information while most simply take data from the returned in-memory objects using the field name and Plain Old Java Object (POJO) patterns to get the data.

DataFetcher requires a DataFetchingEnvironment object which contains information of fetched field.

Understand by an Example

Let’s see an example of Food delivery

type Query {
        food(match : String) : [Food]   # a list of Food
    }

    type Food {
        id : ID
        name : String
        description : String
        cost : Float
        tax : Float
    }

The Food field has a data fetcher, and also each field in the type Food.

The data fetcher on the Food field is likely to be a more complex data fetcher, containing code that goes to a database to get a list of Food objects. It takes an optional match argument and hence can filter these food results if the client specified it.

 DataFetcher FoodDataFetcher = new DataFetcher<List<FoodDTO>>() {
            @Override
            public List<FoodDTO> get(DataFetchingEnvironment environment) {
                DatabaseSecurityCtx ctx = environment.getContext();

                List<FoodDTO> food;
                String match = environment.getArgument("match");
                if (match != null) {
                    food = fetchFoodFromDatabaseWithMatching(ctx, match);
                } else {
                    food = fetchAllFoodFromDatabase(ctx);
                }
                return food;
            }
        };

Once we have value in List<FoodDTO>  food field we typically don’t need specialized data fetchers on each field *(DTO means Data Transfer Object. and since we are mapping the data to schema so it is named as FoodDTO). Graphql ships with a smart property called graphql.schema.PropertyDataFetcher that knows how to follow POJO patterns based on the field name. In the example above there is a name field and hence it will try to look for a public String getName() method to get the data for the name.

PropertyDataFetcher automatically associates with each field by default.

You can however still get access to the graphql.schema.DataFetchingEnvironment in your DTO methods. This allows you to tweak values before sending them out.

  class FoodDTO {

        private ID id;
        private String name;
        private String description;
        private Double cost;
        private Double tax;

        // ...

        public String getName() {
            return name;
        }

        // ...
    }

The interesting parts of the DataFetchingEnvironment

  • <T> T getSource() – the source object is used to get information for a field. It’s the object that is the result of the parent field fetch. Generally, it is an in-memory DTO object and hence simple POJO getters which will provide field values. But sometimes in more complex cases, you may examine it to know how to get the specific information for the current field.
  • getRoot() – The root and the source are the same things for the top-level fields. The root object never changes during the query and it may be null and hence no used.
  • getArguments() – Arguments are those that have been provided on a field. The values of those arguments that have been resolved from passed in variables, AST literals, and default argument values. You use the arguments of a field to control what values it returns.
  • getContext() – When the query is executed at first a Context sets up. It stays the same over the lifetime of the query. The context can be any value and give each data fetcher some calling context needed when trying to get field data.
  • DataFetchingFieldSelectionSet getSelectionSet() – the selection set represents the child fields(“selected” underneath the currently executing field). This can be useful to help look ahead to see what sub-field information a client wants. The following section explains more about this.
  • ExecutionId getExecutionId() – each query execution have a unique id. You can use this perhaps on logs to tag each individual query.

That’s pretty much it from the article. If you have any feedback or queries, please do let me know in the comments. Also, if you liked the article, please give me a thumbs up and I will keep writing blogs like this for you in the future as well. Keep reading and Keep coding.

Reference