Search code examples
javajsongraphqlgraphql-javadataloader

GraphQL Query taking in multiple lists as arguments


This is about adding GraphQL to an existing Java api which takes in multiple lists as input.

Background:

I have an existing Java based REST API getFooInformationByRecognizer which takes in a list of recognizers, where each recognizer object contains an id and it's type and returns information corresponding to each id. The only 3 types possible are A, B or C. The input can be any combination of these types.

Eg:

[{"id": "1", "type": "A" }, {"id": "2", "type": "B"},{"id": "3", "type": "C"}, {"id": "4", "type": "A"}, {"id":"5", "type": "B"}]

Here's it's Java representation:

class FooRecognizer{
     String id;
     String FooType;
}

This api does a bit of processing. First extracts out all the input that has ids of type A and fetches information corresponding to those ids. Similarly, extract out the ids that has type B and fetches information corresponding to those ids and similarly for C. So, it fetches data from 3 different sources and finally collates them to a single map and returns. Eg:

ids of type A --> A SERVICE -> <DATA SOURCE FOR A>
ids of type B --> B SERVICE --> <DATA SOURCE FOR B>
ids of type C --> C SERVICE --> <DATA SOURCE FOR C>

Finally does this: A information + B information + C information and puts this in a Java Hashmap.

The Java representation of the request to this service is:

class FooRequest{
   private Bar bar;
   List<FooRecognizer> list;
}

The Java representation of the response object from the service is:

class FooInformationResponse{
   private Map<String, FooRecognizer> fooInformationCollated;
}

Sample JSON output of the response is:

 "output":{
    "fooInformationCollated":{
    "1":{
       "someProperty": "somePropertyValue"
       "listOfNestedProperties": [{"x": "xValue", "y": "yValue", "z","zValue"]
       "nestedProperty":{
          "anotherProperty":{
            "furtherNestedProperty": "value"
          }
        }
      }
   "2":{
        "someProperty": "somePropertyValue"
        "listOfNestedProperties": [{"a": "aValue", "b": "bValue", "c","cValue"]
        "nestedProperty":{
           "anotherProperty":{
             "furtherNestedProperty": "value"
          }
        }
      }
    }... and so on for other ids in the input

Now, I want to convert this service to GraphQL and here is my query.

query{
  getFooInformationByRecognizer(filterBy:{
    fooRecognizer: [{
            id: "1",
            fooType: A
        }],
        bar: {
            barId: "someId",
            ...<other bar info>
        }
  }){
    fooInformationCollated{
      id
      fooInformation{
        someProperty
        listOfNestedProperties
        nestedProperty{
         anotherProperty{
            furtherNestedProperty
          }
        }

      }
    }
  }
}

Here is my GraphQL schema:

type Query{
    getFooInfoByRecognizer (filterBy: getFooByRecognizerTypeFilter!):getFooByRecognizerTypeFilterResponse
}

input getFooByIdentifierTypeFilter{
    bar: Bar!
    fooIdentifiers: [FooIdentifier!]!
}

input Bar{
    barId: String!
    ....
}


input FooIdentifier{
    id: String!
    fooIdType: fooIdtype!
}

enum fooIdType{
    A
    B
    C
}

I have a few questions here:

  1. Would this be the best way / best practice to represent this query? Or should I model my query to be able to take in 3 separate lists. Eg: query getFooInformationByRecognizer(barId, listOfAs, listOfBs, listOfCs). Any other choice that I have to query / model?
  2. I found having a complex input type as the easiest. In general, is there any specific reason to choose complex input type over other choices or vice-versa?
  3. Is there any thing related to query performance that I should be concerned with? I've tried looking into DataLoader / BatchLoading but that doesn't quite seem to fit the case. I don't think N+1 problem should be an issue as I will also create separate individual resolvers for A, B and C but the query as can be seen does not make further calls to back-end once JSON is returned in response.

Solution

  • The question is too broad to answer concretely, but here's my best attempt.

    1. While there isn't a definitive answer on 1 complex input argument vs multiple simpler arguments, 1 complex argument is generally more desirable as it's easier for the clients to pass a single variable, and it keeps the GraphQL files smaller. This may be more interesting for mutations, but it is a good heuristic regardless. See the logic explained it more detail e.g. in this article.

    2. The logic explained above echoes your own observations

    3. For this specific scenario you listed, I don't see anything of importance for performance. You seem to fetch the whole list in one go (no N+1), so not much different from what you're doing for your REST endpoint. Now, I can't say how expensive it is to fetch the lower-level fields (e.g. whether you need JOINs or network calls or whatever), but if there's any non-trivial logic, you may want to optimize it by looking ahead into the sub-selection before resolving your top-level fields.