Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] How use NamedResolvers with custum execution context #818

Open
u900x600 opened this issue Apr 19, 2023 · 2 comments
Open

[Question] How use NamedResolvers with custum execution context #818

u900x600 opened this issue Apr 19, 2023 · 2 comments

Comments

@u900x600
Copy link

I am trying to create an GQL app following the »NamedResolver« Pattern, as shown in the docs and in this example.

I'd like to access the database Connection within a resolver function from a Reader Context. I cannot find anything the docs and I would be really glad to get some Details on how it is meant to work. I confess to find the types pretty complicated, so I am seeking for help.

Some code:

-- That should be the Context for the resolver
type MyApp = ReaderT Text IO

-- straight from the docs
data User m = User {
      id :: m Text
    , name :: m Text
  } deriving (Generic, GQLType)


-- added MonadReader Text m here
getUser :: (Monad m, MonadReader Text m) => ID -> m (Maybe (User (NamedResolverT m)))
getUser uid = pure $ 
  Just $ 
    User { 
      id = resolve (pure (unpackID uid)), 
      name = resolve (pure ("Test User for: " <> unpackID uid  ))
    }

-- how it should be
-- getUser uid = do
--    db <- get
--    …

instance (MonadReader Text m) => ResolveNamed m (User (NamedResolverT m)) where
  type Dep (User (NamedResolverT m)) = ID
  resolveBatched = traverse getUser

-- straight from the docs
data Query m = Query {
      users :: m [User m]
    , user :: Arg "id" ID -> m (Maybe (User m))
  } deriving (Generic, GQLType)

-- again, added MonadReader m
instance (MonadError GQLError m, MonadReader m) => ResolveNamed m (Query (NamedResolverT m)) where
  type Dep (Query (NamedResolverT m)) = ()
  resolveBatched _ =
    pure
      [ Just
          Query
            { users = resolve (pure ["1325", "2525"]),
              user = \(Arg arg) -> resolve (pure arg)
            }
      ]

rootResolver' :: NamedResolvers MyApp () Query Undefined Undefined
rootResolver' = NamedResolvers

results in that error:

Couldn't match type ‘morpheus-graphql-app-0.27.0:Data.Morpheus.App.Internal.Resolving.ResolverState.ResolverContext’
                 with ‘Text’
    arising from a functional dependency between:
      constraint ‘MonadReader
                    Text
                    (morpheus-graphql-app-0.27.0:Data.Morpheus.App.Internal.Resolving.Resolver.Resolver
                       morpheus-graphql-core-0.27.0:Data.Morpheus.Types.Internal.AST.OperationType.QUERY
                       ()
                       MyApp)’
        arising from a use of ‘NamedResolvers’
      instance ‘MonadReader
                  morpheus-graphql-app-0.27.0:Data.Morpheus.App.Internal.Resolving.ResolverState.ResolverContext
                  (morpheus-graphql-app-0.27.0:Data.Morpheus.App.Internal.Resolving.Resolver.Resolver
                     o e m)’
        at <no location info>
• In the expression: NamedResolvers
  In an equation for ‘rootResolver'’: rootResolver' = NamedResolverstypecheck(-Wdeferred-type-errors)

How should one change the Resolver Context, such that it is possible to access some parameters within the Resolver functions?

I have spent a long time going through the source, the docs and the examples but I was not able to figure out how this is meant to work and I would be happy for any sort of direction.

@nalchevanidze
Copy link
Member

nalchevanidze commented Apr 20, 2023

thanks , will look into the problem.

@u900x600
Copy link
Author

Thanks a lot. I am glad to hear that! I have kept working on that and tried another approach, posted here and the main difference was that I used this definition for the resolution Monad:

module Ctx where


import Data.Text (Text)
import Control.Monad.Reader (ReaderT (runReaderT), MonadReader, MonadIO, runReaderT)
import Control.Monad.Except (ExceptT, MonadError (..), runExceptT)
import Data.Morpheus.Types (GQLError)

type Deps = Text -- represents the database connection
type Fail = GQLError

class (Monad m, MonadError GQLError m, MonadIO m, MonadReader Deps m) => MonadCtx m

newtype Ctx a = Ctx { runCtx :: ReaderT Deps (ExceptT Fail IO) a } 
  deriving (Functor, Applicative, Monad,
            MonadReader Deps,
            MonadError Fail,
            MonadIO,
            MonadCtx)

This way I can write the constraints like that: getUser :: (Ctx.MonadCtx m) => ID -> m (Maybe (User (NamedResolverT m))) , but that didn't change the resulting compile error.

Basically everything compiles, until this is added:

rootResolver :: (Ctx.MonadCtx m) => NamedResolvers m () Query Undefined Undefined
rootResolver = NamedResolvers

I hope that helps! Please note that I am still learning Haskell so it's very well possible that there isn't any issue with code itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants