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!
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.