Skip to content

Instantly share code, notes, and snippets.

@steinybot
Created August 10, 2023 00:18
Show Gist options
  • Save steinybot/3b9f5f8ad638ba8c53fad6b71fe0dfcd to your computer and use it in GitHub Desktop.
Save steinybot/3b9f5f8ad638ba8c53fad6b71fe0dfcd to your computer and use it in GitHub Desktop.
Rant about GraphQL input and output types

graphql/graphql-spec#1038

I'm sure there must be a reason (valid or historical) but distinguishing between input and output types seems to serve no real purpose and comes with a significant cost.

By no means the authority but perhaps a good starting point are the reasons that ChatGPT give:

In GraphQL, the distinction between regular types (also known as output types) and input types serves a specific purpose to ensure clarity, maintainability, and type safety. While it might seem convenient to use any type as an input type, there are valid reasons for this separation:

Please ChatGPT, do tell...

Mutability and Safety: GraphQL input types are meant to represent data that can be consumed by mutations to create or update resources. By enforcing a clear distinction between input types and output types, GraphQL ensures that you explicitly define which types are meant for mutation inputs. This helps prevent accidental mutations of data when querying.

Unless your server side language also has this distinction then there is no safety. Once deserialised then all safety from this is gone. This is especially for a structurally typed language.

It is not GraphQL's job to prevent someone from mutating data when querying. This is a fairly tough problem to solve and it requires some sort of effect system with capability checking (i.e. you can only mutate if you prove you are in a mutable context).

Validation: Input types allow you to define validation rules and constraints specifically for the data being provided as input. This can include mandatory fields, allowed values, and other constraints that are relevant to input data but may not be applicable to output data.

Being able to define validation rules and constraints is the bread and butter of any type system. It is not unique to "input" types. If something is relevant to input data but not output data then it is a different type not a different kind of type.

Serialization and Parsing: GraphQL libraries need to handle serialization (converting GraphQL input into a structured format like JSON) and parsing (converting input data back into GraphQL types). Input types have specialized serialization and parsing logic to handle these operations accurately.

Serialisation and deserialisation is not a property of a type. If you use it as input then you must provide a deserialiser. If you use it as output then you must provide a serialiser. Nothing changes here.

Clear Intent: Separating input types from output types makes the intent of the schema clearer to both the clients and the server. It signals to clients which types can be used for input and which ones are intended for querying.

True but this is not where the intent should come from. If you use it in parentheses (input: MyInput) then it is an input.

Flexibility and Versioning: As your API evolves, you might need to change the structure of input types without affecting the output types. Keeping them separate allows you to evolve your API without breaking existing clients.

If your input type no longer matches your output type then it is just a new type.

Documentation and Client Understanding: By having separate input types, you can provide specific documentation for how to structure input data, which fields are required, and how to use them effectively. This helps clients understand how to interact with your API.

But the same thing applies to output types. The only difference comes from the intent which we get from the usage. If it is input data then the client must provide the required fields. If it is output data then the server must provide the require fields.

There should be no distinction between input and output types. It doubles the complexity of tooling. It limits what we can do and creates unnecessary irregularities (looking at you graphql/graphql-spec#488 and graphql/graphql-spec#825).

If you use a type as input then call it an input type if you wish, heck even suffix it with Input if you want, that is totally up to you (but that it is pointless unless you use a nominally typed language).

Imagine if your programming language forced you to biforcate all your types into those which could be passed into a function and those that could be returned. It would be rediculuous in a nominally typed language and pointless in a structurally typed language yet this is what we find with GraphQL.

If you are unconvinced by any argument made here then that is your right and you should be free to use an @input directive and tooling which supports it but that would leave the rest of us to use types the way they ought to be used.

Now I duck and hide under my desk whilst the bombarding begins and I curse ChatGPT for leading me astray once again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment