haskell-graphql / graphql-api

Write type-safe GraphQL services in Haskell
BSD 3-Clause "New" or "Revised" License
406 stars 35 forks source link

How to throw an error from a handler? #172

Closed harendra-kumar closed 5 years ago

harendra-kumar commented 6 years ago

In a field handler if we figure that the field value does not exist in the database or the argument passed has an invalid value how do we communicate this? We can use a Maybe handler but that does not communicate error properly. I tried grokking this a little bit but could not figure out a way to do this. The documentation, tutorial also does not say anything about error handling.

jml commented 6 years ago

You're right, there isn't a documented way, and I don't know off the top of my head. @teh might have some ideas.

I'd suggest figuring out how to make an Either handler, modeled as a GraphQL Union.

teh commented 6 years ago

I don't have any other ideas either - I think the common pattern is to send errors out-of-band, see e.g. the JS library example here [1]. Maybe this could be done by running a monad on top of IO, e.g. WriterT [Error] IO ()?

[1] https://github.com/kadirahq/graphql-errors#example-error

harendra-kumar commented 6 years ago

The resolver will have to abort when an error occurs and send the error as response. Looking at the int32 HasResolver instance for example:

instance forall m. (Applicative m) => HasResolver m Int32 where
  type Handler m Int32 = m Int32
  resolve handler Nothing = map (ok . toValue) handler
  resolve _ (Just ss) = throwE (SubSelectionOnLeaf ss)

Can we make the handler type an Either instead e.g.:

  type Handler m Int32 = m (Either Error Int32)

So any handler can return either an error or a valid response. If it is an error we can short circuit the resolver and throw an error just like the throwE above. Basically the Handler essentially becomes an ExceptT just like the Handler in servant.

jamesdabbs commented 6 years ago

@harendra-kumar - are you looking to do something like this?

teh commented 5 years ago

fixed by @kquick!