GraphQL Schema to handle mixed types
Asked Answered
R

3

9

I've recently started to research the possibility of using GraphQL for requesting dynamic data configurations. The very first thing that jumps out at me is the strongly-typed concept of GraphQL.

Is there a way for GraphQL schemas to handle arrays of mixed type objects? I would greatly appreciate either an explanation or possibly a reference I can read over.

I am currently working with GraphQL with Node.js but a later implementation will be out of a Java Container. All data will be JSON pulled from MongoDB.

Rote answered 11/12, 2015 at 21:37 Comment(1)
GraphQL has interfaces and unions for this. I don't think I'm qualified to write a proper answer, but you can find it here in the docs: facebook.github.io/graphql/#sec-Interfaces facebook.github.io/graphql/#sec-UnionsPortiere
S
6

You either have to make these disparate types implement the same interface, make your resolvers return unions, or create a custom scalar to hold the dynamic data.

The cleanest approach is the first one: if your resulting objects can be of a limited number of types, define the types so that they implement the same interface, and type your resolvers by the interface. This allows the client to conditionally select sub-fields based on the actual type, and you maintain type safety.

The second approach has similar limitations: you need to know the possible types ahead of time, but they do not have to implement the same interface. It is preferable when the possible values are unrelated to each other and have either/or semantics, like success/failure.

The custom scalar approach is the only one in which you do not need to know the possible types of the result, i.e. the structure of the result can be completely dynamic. Here's an implementation of that approach, known as JSON scalar (i.e. cram any JSON-serializable structure into a scalar value). The big downside of this approach is that it makes sub-selection impossible, as the entire value becomes one big scalar (even though it's a complex object).

Since the question is asking about an array of objects of unknown types, I'll point out that you can, of course, have a list of all the options above.

Examples:

#Interface for any search result
interface SearchResult {
  title: String!
  url: String!
}

#A specific kind of search result
type Book implements SearchResult {
  title: String!
  url: String!
  author: Author!
  isbn: String!
}

type Article implements SearchResult {
  title: String!
  url: String!
  categories: [Category]!
}

type Query {
  #Search can return a mix of Books and Articles
  search(keyword: String!): [SearchResult!]
}

Or

#No interface this time
type Book {
  name: String! #No common fields with Article
  author: Author!
  publisher: Publisher!
}

type Article {
  title: String!
  url: String!
  categories: [Category]!
}

union SearchResult = Book | Article

type Query {
  #Search can return a mix of Books and Articles
  search(keyword: String!): [SearchResult!]
}

Or

scalar JSON

type Query {
  #Search can return anything at all... All bets are off
  search(keyword: String!): [JSON!]
}
Sharpshooter answered 27/5, 2017 at 17:56 Comment(2)
So if I read the documentation, and hopefully understand your answer correctly: It seems impossible to implement a typed schema with an ordered list of mixed results. Example: Mixed search. The union is an "either-or" return type, so you cannot use it, because you want to return mixed results in order of relevance. The interface approach seems out of question, since the resulting types are heterogeneous. Am I seeing right that the JSON scalar is the only option? Would prefer to go back to JSON-API if that is the case...Creolized
Its maybe only the way the documentation is written. I dug further now, and using union types, I could solve my problem. Although, just from reading the documentation, I would not have guessed, union types are the way to go.Creolized
K
2

If data is completely JSON and you would rather preserve them as is, check out JSON scalar type. Basically,

import { GraphQLObjectType } from 'graphql';
import GraphQLJSON from 'graphql-type-json';

export default new GraphQLObjectType({
  name: 'MyType',
  fields: {
    myField: { type: GraphQLJSON },
  },
});
Koehler answered 25/1, 2019 at 22:49 Comment(1)
How can I integrate the filter with the mixed datatype?Dapple
B
-1

I think it's possible to make a custom/generic type that will fit the need. So that way it's still a strong typed array but the type will be flexable enough to set what you need.

Here is an example with custom types: https://github.com/stylesuxx/graphql-custom-types

Biennial answered 30/3, 2016 at 7:27 Comment(1)
This isn't what he's asking for. He's asking if you can make queries to return multiple types in the same json array. Say you want to show a schedule, including both "events" and friends birthdays, you want to handle them as separate objects, because they have different data, and should be displayed differently, but ordered by date.Portiere

© 2022 - 2024 — McMap. All rights reserved.