Help with morpheus graphql example

Hi

I am trying to run the second example from the morpheus graphql package here

The example code is not complete, so I have tried to cobble it together, but I can’t get it to compile.
I’m relativley new to Haskell so maybe I shouldn’t mess with things this advanced, but I really want to get a basic graphql server running so I can experiment with my own logic using simpler Haskell.

Anyway, here’s the code:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

import Data.ByteString.Lazy.Char8 (ByteString)
import Data.Morpheus (interpreter)
import Data.Text (Text)
import Web.Scotty ( body, post, raw, scotty )
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import Data.Morpheus.Types (RootResolver(..), Undefined (..), ResolverQ(..), GQLType, IORes, ResolveQ, liftEither)
import GHC.Generics (Generic)

data Query m = Query
  { deity :: DeityArgs -> m Deity
  } deriving (Generic, GQLType)

data Deity = Deity
  { fullName :: Text         -- Non-Nullable Field
  , power    :: Maybe Text   -- Nullable Field
  } deriving (Generic,GQLType)

data DeityArgs = DeityArgs
  { name      :: Text        -- Required Argument
  , mythology :: Maybe Text  -- Optional Argument
  } deriving (Generic)

resolveDeity :: DeityArgs -> ResolverQ e () Deity
resolveDeity DeityArgs { name, mythology } = liftEither $ askDB name mythology

askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB name _ = Right (Deity {fullName = name, power = Just "foobar"})

rootResolver :: RootResolver IO () Query Undefined Undefined
rootResolver = RootResolver
  { queryResolver        = Query {deity = resolveDeity}
  , mutationResolver     = Undefined
  , subscriptionResolver = Undefined
  }

gqlApi :: ByteString -> IO ByteString
gqlApi = interpreter rootResolver

main :: IO ()
main = scotty 3000 $ post "/api" $ raw =<< (liftIO . gqlApi =<< body)

It fails to compile with the following error, which I sadly can’t make sense of:

• No instance for (GQLType DeityArgs)
        arising from a use of ‘interpreter’
    • In the expression: interpreter rootResolver
      In an equation for ‘api’: api = interpreter rootResolver
  |
50 | api = interpreter rootResolver
  |       ^^^^^^^^^^^^^^^^^^^^^^^^

Any help would be greatly appreciated, I’ve been banging my head against the wall for days now :confused:

I read the example on Hackage again. I think you can just derive GQLType for DeityArgs (deriving (Generic, GQLType) instead of deriving (Generic), but then it is very strange why they don’t do that in the example on Hackage.

Edit: Wait, I get completely different errors from your code, which version of morpheus-graphql are you using? This code works for me:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveAnyClass #-}

module Main where

import Data.ByteString.Lazy.Char8 (ByteString)
import Data.Morpheus (interpreter)
import Data.Text (Text)
import Web.Scotty ( body, post, raw, scotty )
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import Data.Morpheus.Types (RootResolver(..), Undefined (..), ResolverQ(..), GQLType, IORes, ResolveQ, liftEither)
import GHC.Generics (Generic)

data Query m = Query
  { deity :: DeityArgs -> m Deity
  } deriving (Generic, GQLType)

data Deity = Deity
  { fullName :: Text         -- Non-Nullable Field
  , power    :: Maybe Text   -- Nullable Field
  } deriving (Generic, GQLType)

data DeityArgs = DeityArgs
  { name      :: Text        -- Required Argument
  , mythology :: Maybe Text  -- Optional Argument
  } deriving (Generic, GQLType)

resolveDeity :: DeityArgs -> ResolverQ () IO Deity
resolveDeity DeityArgs { name, mythology } = liftEither $ askDB name mythology

askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB name _ = pure $ Right (Deity {fullName = name, power = Just "foobar"})

rootResolver :: RootResolver IO () Query Undefined Undefined
rootResolver = RootResolver
  { queryResolver        = Query {deity = resolveDeity}
  , mutationResolver     = Undefined
  , subscriptionResolver = Undefined
  }

gqlApi :: ByteString -> IO ByteString
gqlApi = interpreter rootResolver

main :: IO ()
main = scotty 3000 $ post "/api" $ raw =<< (liftIO . gqlApi =<< body)

Update: I have made a pull request to fix the example in their readme.

2 Likes

That code works for me too. Thank you so much! :partying_face: