Return HashMap<String, Object> from GraphQL-Java

Simple-Solution picture Simple-Solution · Dec 6, 2017 · Viewed 13.9k times · Source

I tried few variant and had no luck to return a map in GraphQL. So I have the following two objects:

public class Customer {

    private String name, age;
    // getters & setters
}

public class Person {

   private String type;
   private Map<String, Customer> customers;
   // getters & setters
}

My schema looks like this:

type Customer {
   name: String!
   age:  String!
}

type Person {
  type: String!
  customers: [Customer!] // Here I tried all combination but had no luck, is there a Map type support for GQL?
}

Can someone please tell me how to achieve this so that GraphQL magically process this or an alternative approach.

Many thanks!

Answer

kaqqao picture kaqqao · Jan 27, 2018

As you yourself noted, there's no map type in GraphQL, mostly because maps are basically untyped data (or data with a dynamic structure) and, as such, do not translate well into the static types that GraphQL expects. Still, you have a few options.

1) You could change the value type so it includes the key, and give up on the map and use a list instead. This is the approach you took in your own answer. I won't go into detail here as you've already exemplified it.

2) As long as the key and value Java types are known (and not e.g. Object), you can treat a map as list of key-value pairs. You can create a type to represent the pair:

type Person {
  type: String!
  customers: [CustomerEntry!]
}

type CustomerEntry {
  key: String!
  value: Customer!
}

On the down side, you now have uglier queries:

{
   person {
     type
     customers {
       key
       value {
         name
       }
     }
   }
}

On the up side, you keep type safety and (mostly) the semantics. It is possible to keep nesting this approach to e.g. represent a Map<String, Map<Long, Customer>>.

3) If you ever have a completely unknown type, i.e. Object, the only option is to treat it as a complex scalar. In JavaScript, this approach is known as JSON scalar as it boils down to stuffing an arbitrary JSON structure in and treating it as a scalar. The same approach can be implemented in Java. graphql-java now has a project for extended scalars. Here's their ObjectScalar (aliased as JsonScalar) implementation.

Now, if you want to represent a type such as Map<String, Object>, you can opt to represent it using the key-value pair approach from above, with only the value type being the JSON scalar, or you can represent the entire map as a JSON scalar.

As a matter of fact, you can decide to represent any map (well, any type really, but that's not useful) as a JSON scalar.

type MapEntry {
  key: String!
  value: [ObjectScalar!]
}

scalar ObjectScalar

On the upside, you can now keep any dynamic structure's shape exactly. On the downside, since it is a scalar, it is impossible to make sub-selections, and you're stuck fetching it all, without knowing what's inside in advance.