graphqlgraphql-jsgraphql-java

On demand fetching of requested fields in graphql


Is it possible to fields using another query in graphQl ?

For eg., consider I have below schema

type Query {

getVehicles: Vehicles
getCars: [Car!]
getBuses: [Bus!]
getTrucks: [Truck!]
}

type Vehicles {
    cars: [Car!]
    buses: [Bus!]
    trucks: [Truck!]
}
type Car {
    plate: String
}
type Bus {
    plate: String
}
type Truck {
    plate: String
}

Is it possible to have something like

type Vehicles {

cars: getCars()
buses: getBuses()
trucks: getTrucks()
}

so that getCars() will be triggered only when the request field has the cars selected.

I am trying to do an on demand fetching so that we don't have to fetch all 3 when the request contains a sub selection.

I am using com.netflix.graphql.dgs:graphql-dgs-spring-graphql-starter So, my server is implemented using java with com.netflix.graphql.dgs.DgsComponent and com.netflix.graphql.dgs.DgsQuery

edit: Additional question. How can we propagate the input field for first query to the second one ?

getVehicles(state: String!): Vehicles

type Vehicles {
    cars(state: String!): [Car!]
    buses(state: String!): [Bus!]
    trucks(state: String!): [Truck!]
}

@DgsQuery
public Vehicles getVehicles(@InputArgument String state) {
    return new Vehicles(); // Return an empty object; fields are resolved lazily
}

@DgsData(parentType = "Vehicles", field = "cars")
public List<Car> getCars(DgsDataFetchingEnvironment dfe,  @InputArgument String state) {
    System.out.println("Fetching cars...");
    return List.of(new Car("CAR123"), new Car("CAR456"));
}

Solution

  • Your Schema(GraphQL):

    type Query {
      getVehicles: Vehicles
      getCars: [Car!]
      getBuses: [Bus!]
      getTrucks: [Truck!]
    }
    
    
    type Vehicles {
      cars: [Car!]
      buses: [Bus!]
      trucks: [Truck!]
    }
    

    In your GraphQL server, you define resolvers for the Vehicles type like this (Javascript):

    
    const resolvers = {
      Query: {
        getVehicles: () => ({}), // return an empty object or context
      },
      Vehicles: {
        cars: () => getCars(),   // only called if 'cars' is requested
        buses: () => getBuses(), // only called if 'buses' is requested
        trucks: () => getTrucks() // only called if 'trucks' is requested
      }
    };
    

    Example:

    
    query {
      getVehicles {
        cars {
          plate
        }
      }
    }
    

    Only the cars resolver will be triggered in this case.

    Since OP is looking for Java based solution:

    Model Class:

    public class Car {
        private String plate;
      
    }
    
    public class Bus {
        private String plate;
     
    }
    
    public class Truck {
        private String plate;
    
    }
    
    public class Vehicles {
        // optional fields
    }
    

    Resolver (Query)

    @DgsComponent
    public class VehiclesQuery {
    
        @DgsQuery
        public Vehicles getVehicles() {
            return new Vehicles(); // Return an empty object; fields are resolved lazily
        }
    }
    

    Field Resolvers

    @DgsComponent
    public class VehiclesDataFetcher {
    
        @DgsData(parentType = "Vehicles", field = "cars")
        public List<Car> getCars(DgsDataFetchingEnvironment dfe) {
            System.out.println("Fetching cars...");
            return List.of(new Car("CAR123"), new Car("CAR456"));
        }
    
        @DgsData(parentType = "Vehicles", field = "buses")
        public List<Bus> getBuses(DgsDataFetchingEnvironment dfe) {
            System.out.println("Fetching buses...");
            return List.of(new Bus("BUS789"));
        }
    
        @DgsData(parentType = "Vehicles", field = "trucks")
        public List<Truck> getTrucks(DgsDataFetchingEnvironment dfe) {
            System.out.println("Fetching trucks...");
            return List.of(new Truck("TRUCK321"));
        }
    }
    

    Only the getCars() method will be triggered. The others (getBuses, getTrucks) will not be called unless explicitly requested.

    Hopefully, this should resolve your issue