Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return HashMap<String, Object> from GraphQL-Java

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!

like image 363
Simple-Solution Avatar asked Dec 06 '17 12:12

Simple-Solution


People also ask

How do you return an object in GraphQL?

In other words, if you return an empty object ( {} ), an empty array ( [] ) or some other value, GraphQL will treat this as you returning an object and not a null value!

How do I return a map in Java?

HashMap get() Method in Java util. HashMap. get() method of HashMap class is used to retrieve or fetch the value mapped by a particular key mentioned in the parameter. It returns NULL when the map contains no such mapping for the key.

Does GraphQL support map?

GraphQL is a strongly-typed language, and does not provide any kind of map type out of the box. A JSON blob of key-value pairs do not have a strong schema, so you can't have something like this: { key1: val1, key2: val2, key3: val3, ... }

What does string mean in GraphQL?

String! means that the field is non-nullable, meaning that the GraphQL service promises to always give you a value when you query this field.


1 Answers

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.

like image 56
kaqqao Avatar answered Sep 30 '22 04:09

kaqqao