GraphQL errors as unchecked exceptions #39
martinbonnin
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
After reading a lot about the current nullability discussions, I had some kind of realization that at it's core, the problem can be seen as an "exception handling" problem and the good old checked vs unchecked exceptions debate. Let me elaborate a bit.
If we're thinking in Rust Option and Error terms, the current situation is this:
Types that can be represented by the GraphQL type system:
Types that currently cannot be represented by the GraphQL type system:
If I got Strict Semantic Nullability and SemanticNonNull proposals right, they would solve the
Result<String, Error>
by allowing non-null fields to return an error (without bubbling). This is a huge step forward but still feels a bit incomplete in terms of type system as it cannot represent the 4 possible states. Ideally, I'd like the type system to be able to represent everything:Thinking in terms of exceptions
I find it's a lot easier to reason about GraphQL types if we think of errors as exceptions.
The current situation of nullable by default schemas is closer to checked exceptions: the caller has to check for null (== error) when writing UI logic. This is cumbersome, especially when reading the exception requires reading the errors array.
Unchecked GraphQL exceptions
We could think of GraphQL errors as unchecked exceptions. In a nutshell, any field may throw.
Under this premise, the GraphQL type system becomes as easy as a single nullable type modifier:
For the caller, the contract is a lot simpler to reason about:
@catch
For the tools authors, it means more complexity:
@catch
supportThe tradeoff is about more clarity vs more complex tools. I would argue that there are a lot more users than there are tools authors and that if we could make the tooling easy enough to use, the tooling complexity would be amortized quickly. But maybe that's too much complexity? There's a tension here.
IO exceptions vs business errors
Another benefit of thinking in terms of exceptions is that the checked vs unchecked question has been debated a lot, especially in the Java ecosystem, which I happen to know the best.
My current take is very much inspired by this article from Roman Elizarov: https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07
The tldr; is:
I like this distinction. Especially, I like to think that the errors described in the GraphQL best practices belong to that second class of I/O errors. Any error that has a business impact (
BadEmail
,UserDoesNotExists
, etc...) is probably better modeled in the schema.That I/O exception vs business distinction makes it clear when to rely on what.
Questions
Question: what about Rust?
Interestingly, Rust doesn't have exceptions. I'd be curious to know how I/O errors are handled. If you need to parse a JSON where every byte read can fail, how is this handled?
Question: isn't that more complex?
Unchecked exceptions are definitely more complex because we need clients that are error-aware. Software has become more complex over the last decades.
This is good because it gives nicer abstractions to work with (who wants to write assembly code? 😄)
This is also bad because every abstraction has a cost (who wants to buy more RAM to send a slack message?)
Question: what about backward compatibility?
I have absolutely no idea how we reach the endgoal type system without breaking every server and client out there. Problem for another day 😃
Beta Was this translation helpful? Give feedback.
All reactions