Skip to main content

inputUnion type

At a glance

Timeline


Related graphql-js PR: graphql/graphql-js#1196

This is an RFC for a new type: inputUnion.

An inputUnion is a union of one or more input types. It may be used in any location where an input is currently valid. When fulfilling an inputUnion an additional field __inputname must be specified in the map/object fulfilling the input, where the value of __inputname is the name of a single member of the inputUnion being fulfilled.

Example:

input PostInput {
title: String!
body: String!
}
input ImageInput {
photo: String!
caption: String
}

inputUnion MediaBlock = PostInput | ImageInput

type Mutation {
addContent(content: [MediaBlock]!): Post
}

mutation AddContent($content: [MediaBlock]!) {
addContent(content: $content) {
id
}
}

Valid $content value:

[
{__inputname: "PostInput", title: "Hello", content: "World"},
{__inputname: "ImageInput", photo: "http://graphql.org/img/logo.svg", caption: "Logo"}
]

Invalid Value Examples:

{__inputname: "PostInput", title: "Invalid, missing 'content'"}
{title: "Invalid, missing __inputname", content: "World"}
{
__inputname: "PostInput",
title: "Invalid, photo is not defined on PostInput",
content: "World",
photo: "http://graphql.org/img/logo.svg"
}

Checklist:

Are we solving a real problem.

Yes. Many of these problems or use cases are laid out in graphql/graphql-js#207 but to summarize:

When creating input objects, both in mutations and queries you face a tradeoff when creating complex input structs, with one of two options:

  1. Enforce a well typed structure of the input via required fields !. Create multiple endpoints (mutation or query) utilizing these various strict, special case input types.
  2. Loosen the input type requirements and rely on runtime/server-side validation to determine the intended uses.

This solution aims to offer a third path, where more complex combinations of strict input combinations may be utilized, while still keeping the input types fulfillment unambiguous via the __inputname field requirement.

Does this enable new use cases.

Yes. Many of the use cases are detailed in graphql/graphql-js#207. I think the biggest thing this unlocks is the list of heterogeneous inputs, which can be used to define an ordered set of instructions. This also reduces the need for many individual mutations while being able to maintain strictly typed inputs. In my experience tools like apollo-codegen and graphql-code-generator have proven invaluable in creating Flow/TypeScript definitions for validating queries. This change will work well in combination with those tools, making complex input semantics simpler to statically check.

How common is this use case.

Very common. This is the most commented issue in graphql-js, and I personally have run into the tradeoff of creating many highly restrictive mutations vs loosening them up and creating an ad-hoc server implementation. This sort of concept feels like the missing corollary to the expressiveness of the graphql Query execution (single "smart" entry point rather than many ad-hoc endpoints).

Can we enable it without a change to GraphQL.

No, at least not without pushing any type-checking semantics to the execution layer.

Additional thoughts

What about interfaces? There are several comments in related tickets expressing a desire for interfaces in addition to input unions. While it sounds nice for symmetry with querying, I don't see these as being useful or necessary in practice at the input layer. Interfaces are most useful when you wish to query for a generic partial representation of a more specific type. This same requirement does not exist for inputs and it is my opinion that inputInterface would not add enough additional value to justify its addition.

Open questions:

  • Is the use of __inputname a valid option based on spec (__ is reserved for introspection, not sure if we can mirror this for execution)
  • Does __inputname make sense as the name for this?