Return HashMap<String, Object> from GraphQL-Java
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.
There is no map type in GraphQL (Discussion on GitHub).
An alternative approach would be to have customers
as a List
of Customer
s
public class Person {
private String type;
private List<Customer> customers;
}
and include the key for the map inside the Customer
class
public class Customer {
private String key; // or another meaningful name
private String name, age;
}
Schema would mostly remain the same.
type Customer {
key: String! // or another meaningful name
name: String!
age: String!
}
type Person {
type: String!
customers: [Customer!]!
}