morpheus-graphql
Morpheus GraphQL
| Version on this page: | 0.17.0 | 
| LTS Haskell 23.28: | 0.28.1 | 
| Stackage Nightly 2025-05-05: | 0.28.1 | 
| Latest on Hackage: | 0.28.1 | 
morpheus-graphql-0.17.0@sha256:487ad2e2b11d1038a65068f139f291a4e7b00986c347132b56e141608f0c8789,29450Module documentation for 0.17.0
Morpheus GraphQL  
 
Build GraphQL APIs with your favorite functional language!
Morpheus GraphQL (Server & Client) helps you to build GraphQL APIs in Haskell with native Haskell types. Morpheus will convert your Haskell types to a GraphQL schema and all your resolvers are just native Haskell functions. Morpheus GraphQL can also convert your GraphQL Schema or Query to Haskell types and validate them in compile time.
Morpheus is still in an early stage of development, so any feedback is more than welcome, and we appreciate any contribution! Just open an issue here on GitHub, or join our Slack channel to get in touch.
Getting Started
Setup
To get started with Morpheus, you first need to add it to your project’s dependencies, as follows (assuming you’re using hpack):
package.yml
dependencies:
  - morpheus-graphql
Additionally, you should tell stack which version to pick:
stack.yml
resolver: lts-16.2
extra-deps:
  - morpheus-graphql-0.17.0
As Morpheus is quite new, make sure stack can find morpheus-graphql by running stack upgrade and stack update
Building your first GraphQL API
with GraphQL syntax
schema.gql
type Query {
  deity(name: String! = "Morpheus"): Deity!
}
"""
Description for Deity
"""
type Deity {
  """
  Description for name
  """
  name: String!
  power: String @deprecated(reason: "some reason for")
}
API.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module API (api) where
import Data.ByteString.Lazy.Char8 (ByteString)
import Data.Morpheus (interpreter)
import Data.Morpheus.Document (importGQLDocument)
import Data.Morpheus.Types (RootResolver (..), Undefined (..))
import Data.Text (Text)
importGQLDocument "schema.gql"
rootResolver :: RootResolver IO () Query Undefined Undefined
rootResolver =
  RootResolver
    { queryResolver = Query {deity},
      mutationResolver = Undefined,
      subscriptionResolver = Undefined
    }
  where
    deity DeityArgs {name} =
      pure
        Deity
          { name = pure name,
            power = pure (Just "Shapeshifting")
          }
api :: ByteString -> IO ByteString
api = interpreter rootResolver
Template Haskell Generates types: Query , Deity, DeityArgs, that can be used by rootResolver
descriptions and deprecations will be displayed in introspection.
importGQLDocumentWithNamespace will generate Types with namespaced fields. If you don’t need namespace use importGQLDocument
with Native Haskell Types
To define a GraphQL API with Morpheus we start by defining the API Schema as a native Haskell data type,
which derives the Generic typeClass. Lazily resolvable fields on this Query type are defined via a -> ResolverQ () IO b, representing resolving a set of arguments a to a concrete value b.
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)
For each field in the Query type defined via a -> m b (like deity) we will define a resolver implementation that provides the values during runtime by referring to
some data source, e.g. a database or another API. Fields that are defined without a -> m b you can just provide a value.
In above example, the field of DeityArgs could also be named using reserved identities (such as: type, where, etc), in order to avoid conflict, a prime symbol (') must be attached. For example, you can have:
data DeityArgs = DeityArgs
  { name      :: Text        -- Required Argument
  , mythology :: Maybe Text  -- Optional Argument
  , type'     :: Text
  } deriving (Generic)
The field name in the final request will be type instead of type'. The Morpheus request parser converts each of the reserved identities in Haskell 2010 to their corresponding names internally. This also applies to selections.
resolveDeity :: DeityArgs -> ResolverQ e () Deity
resolveDeity DeityArgs { name, mythology } = liftEither $ dbDeity name mythology
askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB = ...
To make this Query type available as an API, we define a RootResolver and feed it to the Morpheus interpreter. A RootResolver consists of query, mutation and subscription definitions, while we omit the latter for this example:
rootResolver :: RootResolver IO () Query Undefined Undefined
rootResolver =
  RootResolver
    { queryResolver = Query {deity = resolveDeity}
    , mutationResolver = Undefined
    , subscriptionResolver = Undefined
    }
gqlApi :: ByteString -> IO ByteString
gqlApi = interpreter rootResolver
As you can see, the API is defined as ByteString -> IO ByteString which we can either invoke directly or use inside an arbitrary web framework
such as scotty or serverless-haskell. We’ll go for scotty in this example:
main :: IO ()
main = scotty 3000 $ post "/api" $ raw =<< (liftIO . gqlApi =<< body)
If we now send a POST request to http://localhost:3000/api with a GraphQL Query as body for example in a tool like Insomnia:
query GetDeity {
  deity (name: "Morpheus") {
    fullName
    power
  }
}
our query will be resolved!
{
  "data": {
    "deity": {
      "fullName": "Morpheus",
      "power": "Shapeshifting"
    }
  }
}
Serverless Example
If you are interested in creating a Morpheus GraphQL API with Serverless, you should take a look at our example in this repository:
Mythology API it is our example project build with Morpheus GraphQL and Serverless-Haskell,
where you can query different mythology characters with GraphiQL.
Mythology API is deployed on : api.morpheusgraphql.com where you can test it with GraphiQL

Advanced topics
Enums
You can use Union Types as Enums, but they’re not allowed to have any parameters.
data City
  = Athens
  | Sparta
  | Corinth
  | Delphi
  | Argos
  deriving (Generic)
instance GQLType City where
  type KIND City = ENUM
Union types
To use union type, all you have to do is derive the GQLType class. Using GraphQL fragments, the arguments of each data constructor can be accessed from the GraphQL client.
data Character
  = CharacterDeity Deity -- will be unwrapped, since Character + Deity = CharacterDeity
  | SomeDeity Deity -- will be wrapped since Character + Deity != SomeDeity
  | Creature { creatureName :: Text, creatureAge :: Int }
  | Demigod Text Text
  | Zeus
  deriving (Generic, GQLType)
where Deity is an object.
As we see, there are different kinds of unions. Morpheus handles them all.
This type will be represented as
union Character = Deity | SomeDeity | Creature | SomeMulti | Zeus
type SomeDeity {
  _0: Deity!
}
type Creature {
  creatureName: String!
  creatureAge: Int!
}
type Demigod {
  _0: Int!
  _1: String!
}
type Zeus {
  _: Unit!
}
By default, union members will be generated with wrapper objects. There is one exception to this: if a constructor of a type is the type name concatenated with the name of the contained type, it will be referenced directly. That is, given:
data Song = { songName :: Text, songDuration :: Float } deriving (Generic, GQLType)
data Skit = { skitName :: Text, skitDuration :: Float } deriving (Generic, GQLType)
data WrappedNode
  = WrappedSong Song
  | WrappedSkit Skit
  deriving (Generic, GQLType)
data NonWrapped
  = NonWrappedSong Song
  | NonWrappedSkit Skit
  deriving (Generic, GQLType)
You will get the following schema:
# has wrapper types
union WrappedNode = WrappedSong | WrappedSkit
# is a direct union
union NonWrapped = Song | Skit
type WrappedSong {
  _0: Song!
}
type WrappedSKit {
  _0: Skit!
}
type Song {
  songDuration: Float!
  songName: String!
}
type Skit {
  skitDuration: Float!
  skitName: String!
}
- 
for all other unions will be generated new object type. for types without record syntax, fields will be automatically indexed. 
- 
all empty constructors in union will be summed in type <tyConName>Enum(e.gCharacterEnum), this enum will be wrapped inCharacterEnumObjectand added to union members.
Scalar types
To use custom scalar types, you need to provide implementations for parseValue and serialize respectively.
data Odd = Odd Int  deriving (Generic)
instance DecodeScalar Euro where
  decodeScalar (Int x) = pure $ Odd (... )
  decodeScalar _ = Left "invalid Value!"
instance EncodeScalar Euro where
  encodeScalar (Odd value) = Int value
instance GQLType Odd where
  type KIND Odd = SCALAR
Applicative and Monad instance
The Resolver type has Applicative and Monad instances that can be used to compose resolvers.
Introspection
Morpheus converts your schema to a GraphQL introspection automatically. You can use tools like Insomnia to take a
look at the introspection and validate your schema.
If you need a description for your GQLType inside of the introspection you can define the GQLType instance manually
and provide an implementation for the description function:
data Deity = Deity
{ ...
} deriving (Generic)
instance GQLType Deity where
  description = const "A supernatural being considered divine and sacred"
screenshots from Insomnia
 
 

Handling Errors
for errors you can use use either liftEither or MonadFail:
at the and they have same result.
with liftEither
resolveDeity :: DeityArgs -> ResolverQ e IO Deity
resolveDeity DeityArgs {} = liftEither $ dbDeity
dbDeity ::  IO Either Deity
dbDeity = pure $ Left "db error"
with MonadFail
resolveDeity :: DeityArgs -> ResolverQ e IO Deity
resolveDeity DeityArgs { } = fail "db error"
Mutations
In addition to queries, Morpheus also supports mutations. They behave just like regular queries and are defined similarly:
newtype Mutation m = Mutation
  { createDeity :: MutArgs -> m Deity
  } deriving (Generic, GQLType)
rootResolver :: RootResolver IO  () Query Mutation Undefined
rootResolver =
  RootResolver
    { queryResolver = Query {...}
    , mutationResolver = Mutation { createDeity }
    , subscriptionResolver = Undefined
    }
    where
      -- Mutation Without Event Triggering
      createDeity :: MutArgs -> ResolverM () IO Deity
      createDeity_args = lift setDBAddress
gqlApi :: ByteString -> IO ByteString
gqlApi = interpreter rootResolver
Subscriptions
In morpheus subscription and mutation communicate with Events,
Event consists with user defined Channel and Content.
Every subscription has its own Channel by which it will be triggered
data Channel
  = ChannelA
  | ChannelB
data Content
  = ContentA Int
  | ContentB Text
type MyEvent = Event Channel Content
newtype Query m = Query
  { deity :: m Deity
  } deriving (Generic)
newtype Mutation m = Mutation
  { createDeity :: m Deity
  } deriving (Generic)
newtype Subscription (m ::  * -> * ) = Subscription
  { newDeity :: m  Deity
  } deriving (Generic)
newtype Subscription (m :: * -> *) = Subscription
{ newDeity :: SubscriptionField (m Deity),
}
deriving (Generic)
type APIEvent = Event Channel Content
rootResolver :: RootResolver IO APIEvent Query Mutation Subscription
rootResolver = RootResolver
  { queryResolver        = Query { deity = fetchDeity }
  , mutationResolver     = Mutation { createDeity }
  , subscriptionResolver = Subscription { newDeity }
  }
 where
  -- Mutation Without Event Triggering
  createDeity :: ResolverM EVENT IO Address
  createDeity = do
      requireAuthorized
      publish [Event { channels = [ChannelA], content = ContentA 1 }]
      lift dbCreateDeity
  newDeity :: SubscriptionField (ResolverS EVENT IO Deity)
  newDeity = subscribe ChannelA $ do
    -- executed only once
    -- immediate response on failures
    requireAuthorized
    pure $ \(Event _ content) -> do
        -- executes on every event
        lift (getDBAddress content)
Interface
- 
defining interface with Haskell Types (runtime validation): -- interface is just regular type derived as interface newtype Person m = Person {name :: m Text} deriving (Generic) instance GQLType (Person m) where type KIND (Person m) = INTERFACE -- with GQLType user can links interfaces to implementing object instance GQLType Deity where implements _ = [interface (Proxy @Person)]
- 
defining with importGQLDocumentandDSL(compile time validation):interface Account { name: String! } type User implements Account { name: String! }
Morpheus GraphQL Client with Template haskell QuasiQuotes
defineByDocumentFile
    "./schema.gql"
  [gql|
    query GetHero ($character: Character)
      {
        deity (fatherOf:$character) {
          name
          power
          worships {
            deity2Name: name
          }
        }
      }
  |]
with schema:
input Character {
  name: String!
}
type Deity {
  name: String!
  worships: Deity
  power: Power!
}
enum Power {
  Lightning
  Teleportation
  Omniscience
}
will validate query and Generate:
- namespaced response and variable types
- instance for FetchtypeClass
data GetHero = GetHero {
  deity: DeityDeity
}
-- from: {user
data DeityDeity = DeityDeity {
  name: Text,
  worships: Maybe DeityWorshipsDeity
  power: Power
}
-- from: {deity{worships
data DeityWorshipsDeity = DeityWorshipsDeity {
  name: Text,
}
data Power =
    PowerLightning
  | PowerTeleportation
  | PowerOmniscience
data GetHeroArgs = GetHeroArgs {
  character: Character
}
data Character = Character {
  name: Person
}
as you see, response type field name collision can be handled with GraphQL alias.
with fetch you can fetch well typed response GetHero.
  fetchHero :: Args GetHero -> m (Either String GetHero)
  fetchHero = fetch jsonRes args
      where
        args = GetHeroArgs {character = Person {name = "Zeus"}}
        jsonRes :: ByteString -> m ByteString
        jsonRes = <GraphQL APi>
in this case, jsonRes resolves a request into a response in some monad m.
A fetch resolver implementation against a real API may look like the following:
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Char8 as C8
import Network.HTTP.Req
resolver :: String -> ByteString -> IO ByteString
resolver tok b = runReq defaultHttpConfig $ do
    let headers = header "Content-Type" "application/json"
    responseBody <$> req POST (https "swapi.graph.cool") (ReqBodyLbs b) lbsResponse headers
this is demonstrated in examples/src/Client/StarWarsClient.hs
types can be generated from introspection too:
defineByIntrospectionFile "./introspection.json"
Morpheus CLI for Code Generating
you should use morpheus-graphql-cli
Showcase
Below are the list of projects using Morpheus GraphQL. If you want to start using Morpheus GraphQL, they are good templates to begin with.
- https://github.com/morpheusgraphql/mythology-api
- Serverless Mythology API
 
- https://github.com/dandoh/web-haskell
- Modern webserver boilerplate in Haskell: Morpheus Graphql + Postgresql + Authentication + DB migration + Dotenv and more
 
Edit this section and send PR if you want to share your project.
About
The name
Morpheus is the greek god of sleep and dreams whose name comes from the greek word μορφή meaning form or shape. He is said to be able to mimic different forms and GraphQL is good at doing exactly that: Transforming data in the shape of many different APIs.
Team
Morpheus is written and maintained by nalchevanidze
Roadmap
- Medium future:
- Stabilize API
- Specification-isomorphic error handling
 
- Long term:
- Support all possible GQL features
- Performance optimization
 
Changes
Changelog
0.17.0 - 25.02.2021
new features
- 
(issue #543 & #558): GQLTypeOptionssupports new optiontypeNameModifier. Before the schema failed if you wanted to use the same type for input and output, and the user had no control over the eventual GraphQL type name of the generated schema. Now with this option you can provide a function of typeBool -> String -> Stringthat generates a custom GraphQL type name. The first argument is aBoolthat isTrueif the type is an input, andFalseotherwise. The second argument is aStringrepresenting the initial, auto-generated GraphQL type name. The function returns the desired type name. thanks @nalchevanidze & @bradshermane.g this schema will not fail. morpheus will generate types: DeityandInputDeitydata Deity = Deity { name :: Text, age :: Int } deriving (Show, Generic) deityTypeNameModifier isInput original | isInput = "Input" ++ original | otherwise = original instance GQLType Deity where typeOptions _ opt = opt {typeNameModifier = deityTypeNameModifier} newtype DeityArgs = DeityArgs { input :: Deity } deriving (Show, Generic, GQLType) newtype Query (m :: * -> *) = Query { deity :: DeityArgs -> m Deity } deriving (Generic, GQLType)
- 
exposed EncodeWrapperandDecodeWrappertype-classes.
Breaking Changes
- 
Map k vis now represented as just[Pair k v]
- 
GQLScalarwas replaced withEncodeScalarandDecodeScalartype-classes.
- 
Exclusive input objects: Sum types used as input types are represented as input objects, where only one field must have a value. Namespaced constructors (i.e., where referenced type name concatenated with union type name is equal to constructor name) are unpacked. Furthermore, empty constructors are represented as fields with the unit type. for example: data Device | DevicePC PC | Laptops { macAdress :: ID } | Smartphonethis type will generate the following SDL: enum Unit { Unit } input Laptop { macAdress: ID } input Device { PC: PC Laptops: Laptops Smartphone: Unit }
- 
For each nullary constructor will be defined GQL object type with a single field _: Unit(since GraphQL does not allow empty objects).for example: data Person = Client { name :: Text } | Accountant | Developerthis type will generate the following SDL: enum Unit { Unit } type Student { name: String! } type Accountant { _: Unit! } type Developer { _: Unit! } union Person = Client | Accountant | Developer
- 
changed signature of GQLType.typeOptionsfromf a -> GQLTypeOptionstof a -> GQLTypeOptions -> GQLTypeOptions.now you can write: typeOptions _ options = options { fieldLabelModifier = <my function> }
whre argument options is default gql options.
- deexposed constructor of GQLTypeOptions.
- Type name for parametrized types like One (Two Three)will be generated directly, concatenating themOneTwoThreeinstead ofOne_Two_Three.
- Haskell Floatwas renamed to custom scalar typeFloat32.
- Haskell Doublenow represents GraphQLFloat.
Minor Changes
- deprecated kinds INPUT,ENUMandOUTPUTin favor of more generalized kindTYPE. now you can derive INPUT, ENUM and OUTPUT automatically withderiving (Generic, GQLType).
- more likely to rebuild when a file loaded by importGQLDocumentorimportGQLDocumentWithNamespaceis changed
0.16.0 - 05.11.2020
Breaking changes
- subscriptions are extracted in morpheus-graphql-subscriptions.
- Event,- httpPubAppand- webSocketsAppmoved- Data.Morpheus.Subscriptions
New Features
- 
Data.Morpheus.Subscriptionsprovides:- runPubApp: generalized version of httpPubApp
- runSubApp: generalized version of webSocketsApp
 
- runPubApp: generalized version of 
- 
New encode and decode instances for Set,NonEmpty,SeqandVectorSetandNonEmptythrow a graphql error when a duplicate is found (Set) or when an empty list is sent (NonEmpty). Beware: Right now, all these types are advertised as lists in the introspection query. This is something we are trying to change by submitting a proposal to the graphql spec.
Minor Changes
- parser performance optimization
0.15.1 - 12.09.2020
0.15.0 - 12.09.2020
new features
- 
custom operation root types: e.g RootResolver IO () MyQuery MyMutation Undefinedcreates app with: schema { query: MyQuery mutation: MyMutation }
- 
type : App event mandderiveAppapp :: App EVENT IO app = runApp (deriveApp root) api :: a -> IO b api = runApp (deriveApp root)
- 
Appsupports semigroup(schema Stitching): if whe have two graphql appsmergedApi :: a -> m b mergedApi = runApp (deriveApp root <> deriveApp root2)
- 
GQLTypeexposestypeOptionsto modify labels:typeOptions :: f a -> GQLTypeOptionswhere GQLTypeOptions { fieldLabelModifier :: String -> String, constructorTagModifier :: String -> String }
- 
you can use GQLType.getDescriptionsto document field or enum Values
- 
with importGQLDocumentWithNamespacenow you can use Enums with Colliding Values:enum X { A } enum Y { A }they will be namespaced to. XAandYA
Breaking Changes
- 
importGQLDocumentWithNamespacethey will be namespaced enum Values
- 
Argument types must have GQLTypeinstances
- 
in Data.Morpheus.Server:- removed subscriptionApp
- changed webSocketsApptype toApp e m -> m (ServerApp, e -> m ())
- changed httpPubApptype to[e -> m ()] -> App e m -> a -> m b
 
- removed 
- 
removed StreamfromData.Morpheus.Types
- 
removed class Interpreter,interpreteris now just regular function.interpreter = runApp . deriveApp
Minor Changes
- internal refactoring
0.14.1 - 16.08.2020
0.14.0 - 15.08.2020
new features
- query validation supports interfaces
- debugInterpreter: displays internal context on graphql errors
- compileTimeSchemaValidation :
morpheus validates schema at runtime (after the schema derivation).
to be ensure that only correct api is compiled.
we can use template haskell method compileTimeSchemaValidation
import Morpheus.Graphql.Server(compileTimeSchemaValidation)
_validateSchema :: ()
_validateSchema = $(compileTimeSchemaValidation (Identity gqlRoot))
- 
directive Validation for Document (TypeSystem). 
- 
supports of block string values. e.g: query { createDeity( name: """ power qwe bla \n sd blu \\ date """ ) { name } }
- 
Data.Morpheus.DocumentexposesRootResolverConstraint
- 
Data.Morpheus.ServerexposeshttpPlayground
- 
httpPubAppsupportsGQLRequest -> GQLResponse
- 
morpheus-graphql-coresupport ofschema. issue #412schema { query: Query }note that this does not affect morpheus-graphql-serverat all. since it has its own schema derivation. you still need to provide:rootResolver :: RootResolver () IO Query Undefined Undefined rootResolver = RootResolver <resolvers ...>
- 
Subscription Resolver supports Monad.
- 
nested Subscription Resolvers. 
Breaking Changes
- Context' renamed toResolverContext’
- internal refactoring: changed AST
- root subscription fields must be wrapped with SubscriptionField. e.g:
data Subscription (m :: * -> *) = Subscription
{ newDeity :: SubscriptionField (m Deity),
  newHuman :: HumanArgs -> SubscriptionField (m Human)
}
deriving (Generic)
- signature of subscribeis changed. now you can use it as followed:
resolveNewAddress :: SubscriptionField (ResolverS EVENT IO Address)
resolveNewAddress = subscribe ADDRESS $ do
    -- executed only once
    -- immediate response on failures
    requireAuthorized
    pure $ \(Event _ content) -> do
        -- executes on every event
        lift (getDBAddress content)
- removed from Data.Morpheus.Types- SubField
- ComposedSubField
 
0.13.0 - 22.06.2020
breaking changes
- renamed GQLRootResolver->RootResolver
new features
- importGQLDocumentautomatically defines- GQLTypeinstances for scalar definitions
- supports default values
0.12.0 - 21.05.2020
Breaking Changes
Package was extracted as:
- 
morpheus-graphql-core: core components like: parser, validator, executor, utils.
- 
Data.Morpheus.Core 
- 
Data.Morpheus.QuasiQuoter 
- 
Data.Morpheus.Error 
- 
Data.Morpheus.Internal.TH 
- 
Data.Morpheus.Internal.Utils 
- 
Data.Morpheus.Types.Internal.Resolving 
- 
Data.Morpheus.Types.Internal.Operation 
- 
Data.Morpheus.Types.Internal.AST 
- 
Data.Morpheus.Types.IO 
- 
morpheus-graphql-client: lightweight version of morpheus client without server implementation
- 
Data.Morpheus.Client 
- 
morpheus-graphql: morpheus graphql server
- 
Data.Morpheus 
- 
Data.Morpheus.Kind 
- 
Data.Morpheus.Types 
- 
Data.Morpheus.Server 
- 
Data.Morpheus.Document 
deprecated:
- Res,- IORes,- ResolveQ: use- ResolverQ
- MutRes,- IOMutRes,- ResolveM: use- ResolverM
- SubRes,- IOSubRes,- ResolveS: use- ResolverS
- failRes: use- MonadFail
New Feature
- Semigroupsupport for Resolver
- MonadFailSupport for Resolver
- flexible resolvers: ResolverO,ResolverQ,ResolverM,ResolverSthey can handle object and scalar types:
-- if we have record and regular Int
data Object m = Object { field :: m Int }
-- we can write
-- handles kind : (* -> *) -> *
resolveObject :: ResolverO o EVENT IO Object
-- is alias to: Resolver o () IO (Object (Resolver o () IO))
-- or
-- handles kind : *
resolveInt :: ResolverO o EVENT IO Int
-- is alias to: Resolver o () IO Int
the resolvers : ResolverQ , ResolverM, ResolverS , are like
ResolverO but with QUERY , MUTATION and SUBSCRIPTION as argument.
- 
flexible composed Resolver Type alias: ComposedResolver. extendsResolverOwith parameter(f :: * -> *). so that you can compose Resolvers e.g:resolveList :: ComposedResolver o EVENT IO [] Object -- is alias to: Resolver o () IO [Object (Resolver o () IO))] resolveList :: ComposedResolver o EVENT IO Maybe Int -- is alias to: Resolver o () IO (Maybe Int)
- 
server supports interfaces (see Readme): - define interface with Haskell Types (runtime validation):
- define interface with importGQLDocumentandDSL(compile time validation):
 
- 
support default directives: @skipand@include
- 
SelectionTree interface 
minor
- fixed subscription sessions, starting new session does not affects old ones.
- added tests for subscriptions
0.11.0 - 01.05.2020
Breaking Changes
- 
Client generated enum data constructors are now prefixed with with the type name to avoid name conflicts. 
- 
for Variant selection inputUnion uses inputnameinstead of__typename
- 
in Data.Morpheus.Server- gqlSocketAppand- gqlSocketMonadIOAppare replaced with- webSocketsApp
- removed initGQLState,GQLState
 
- 
for better control of subscriptions - replaced instance interpreter gqlRoot statewithinterpreter gqlRoot.
- added: Input,Stream,httpPubApp
 from now on you can define API that can be used in websockets as well as in http servers api :: Input api -> Stream api EVENT IO api = interpreter gqlRoot server :: IO () server = do (wsApp, publish) <- webSocketsApp api let httpApp = httpPubApp api publish ... runBoth wsApp httpAppwhere publish :: e -> m ()websockets and http app do not have to be on the same server. e.g. you can pass events between servers with webhooks. 
- replaced instance 
- 
subscription can select only one top level field (based on the GraphQL specification). 
New features
- Instead of rejecting conflicting selections, they are merged (based on the GraphQL specification).
- Support for input lists separated by newlines. thanks @charlescrain
- conflicting variable , fragment … validation
- issue #411: Aeson FromJSONToJSONinstances forID
minor
- changes to internal types
- fixed validation of apollo websockets requests
0.10.0 - 07.01.2020
Breaking Changes
- 
all constructors of Resolver:QueryResolver,MutResolver,SubResolverare unexposed. uselift,publishorsubscribe. e.g-- Query Resolver resolveUser :: ResolveQ EVENT IO User resolveUser = lift getDBUser -- Mutation Resolver resolveCreateUser :: ResolveM EVENT IO User resolveCreateUser = do publish [userUpdate] -- publishes event inside mutation lift setDBUser -- Subscription Resolver resolveNewUser :: ResolveS EVENT IO User resolveNewUser = subscribe [USER] $ do pure $ \(Event _ content) -> lift (getDBUserByContent content)
New features
- 
exposed publishfor mutation resolvers, now you can writeresolveCreateUser :: ResolveM EVENT IO User resolveCreateUser = do requireAuthorized publish [userUpdate] liftEither setDBUser
- 
exposed subscribefor subscription resolvers, now you can writeresolveNewUser :: ResolveS EVENT IO User resolveNewUser = subscribe [USER] $ do requireAuthorized pure userByEvent where userByEvent (Event _ content) = liftEither (getDBUser content)
- 
type SubFieldwill convert your subscription monad to query monad.SubField (Resolver Subscription Event IO) Userwill generate same asResolver Subscription Event IO (User ((Resolver QUERY Event IO)))now if you want define subscription as follows data Subscription m = Subscription { newUser :: SubField m User }
- 
unsafeInternalContextto get resolver context, use only if it really necessary. the code depending on it may break even on minor version changes.resolveUser :: ResolveQ EVENT IO User resolveUser = do Context { currentSelection, schema, operation } <- unsafeInternalContext lift (getDBUser currentSelection)
minor
- monad instance for resolvers. thanks @dandoh
- example using stm, authentication, monad transformers. thanks @dandoh
- added dependency mtl
[0.9.1] - 02.01.2020
- removed dependency mtl
[0.9.0] - 02.01.2020
Added
- WithOperationconstraint for Generic Resolvers (#347) thanks @dandoh
Fixed
- 
liftEither support in MutResolver (#351) 
- 
selection of __typenameon object und union objects (#337)
- 
auto inference of external types in gql document (#343) th will generate field m (Type m)if type has an argumente.g for this types and DSL data Type1 = Type1 { ... } type Type2 m = SomeType m data Type3 m = Type2 { bla :: m Text } deriving ...type Query { field1: Type1! field2: Type2! field3: Type3! }morpheus generates data Query m = Query { field1 :: m Type1 field2 :: m (Type2 m) field3 :: m (Type3 m) } deriving ...now you can combine multiple gql documents: importDocumentWithNamespace `coreTypes.gql` importDocumentWithNamespace `operations.gql`
Changed
- 
support of resolver fields m typefor the fields without argumentsdata Deity m = Deity { name :: m Text } -- is equal to data Deity m = Deity { name :: () -> m Text }
- 
template haskell generates m typeinstead of() -> m typefor fields without argument (#334)data Deity m = Deity { name :: (Arrow () (m Text)), power :: (Arrow () (m (Maybe Text))) } -- changed to data Deity m = Deity { name :: m Text, power :: m (Maybe Text) }
[0.8.0] - 15.12.2019
Changed
- 
deprecated: INPUT_OBJECT,OBJECT,UNION,- use INPUTinstead ofINPUT_OBJECT
- use deriving(GQLType)instead ofOBJECTorUNION
 
- use 
- 
only namespaced Unions generate regular graphql Union, other attempts will be wrapped inside an object with constructor name : e.g: data Character = CharacterDeity Deity SomeDeity Deity deriving (GQLType)where Deityis Object. will generateunion CHaracter = Deity | SomeDeity type SomeDeity { _0: Deity }
Added
- failResfor resolver failures
- added kind: INPUT , OUTPUT
- Automatic Type Inference (only for Object, Union and Enum)
- More general stateful resolvers which accept instances of MonadIO (Authored by Sebastian Pulido [sebashack])
- Utility to create web-socket applications with custom MonadIO instances (Authored by Sebastian Pulido [sebashack])
data Realm  =
    Sky
  | Sea
  | Underworld
    deriving (Generic, GQLType)
data Deity  = Deity{
    fullName:: Text,
    realm:: Realm
  } deriving (Generic, GQLType)
data Character  =
    CharacterDeity Deity -- Only <tyConName><conName> should generate direct link
  -- RECORDS
  | Creature { creatureName :: Text, creatureAge :: Int }
  --- Types
  | SomeDeity Deity
  | CharacterInt Int
  | SomeMulti Int Text
  --- ENUMS
  | Zeus
  | Cronus deriving (Generic, GQLType)
will generate schema:
enum Realm {
  Sky
  Sea
  Underworld
}
type Deity {
  fullName: String!
  realm: Realm!
}
union Character =
    Deity
  | Creature
  | SomeDeity
  | CharacterInt
  | SomeMulti
  | CharacterEnumObject
type Creature {
  creatureName: String!
  creatureAge: Int!
}
type SomeDeity {
  _0: Deity!
}
type CharacterInt {
  _0: Int!
}
type SomeMulti {
  _0: Int!
  _1: String!
}
# enum
type CharacterEnumObject {
  enum: CharacterEnum!
}
enum CharacterEnum {
  Zeus
  Cronus
}
rules:
- 
haskell union type with only empty constructors (e.g Realm), will generate graphqlenum
- 
haskell record without union (e.g Deity), will generate graphqlobject
- 
namespaced Unions: CharacterDeitywhereCharacteris TypeConstructor andDeityreferenced object (not scalar) type: will be generate regular graphql Unionunion Character = Deity | ...
- 
for union records ( Creature { creatureName :: Text, creatureAge :: Int }) will be referenced in union type, plus typeCreaturewill be added in schema.e.g union Character = ... | Creature | ... type Creature { creatureName : String! creatureAge: Int! }- 
all empty constructors in union will be summed in type <tyConName>Enum(e.gCharacterEnum), this enum will be wrapped inCharacterEnumObjectand this type will be added to unionCharacter. as in example above
- 
there is only types left with form TypeName Type1 2Type ..(e.gSomeDeity Deity,CharacterInt Int,SomeMulti Int Text),morpheus will generate objet type from it: type TypeName { _0: Type1! _1: Type2! ... }
 
- 
Removed
- removed kind: INPUT_UNION
Fixed
- on filed resolver was displayed. Unexhausted case exception of graphql error
- support of signed numbers (e.g -4)
- support of round floats (e.g 1.000)
- validation checks undefined fields on inputObject
- variables are supported inside input values
[0.7.1] - 26.11.2019
- max bound includes: support-megaparsec-8.0
[0.7.0] - 24.11.2019
Removed
- toMorpheusHaskellAPifrom- Data.Morpheus.Documentfunctionality will be migrated in- morpheus-graphql-cli
Changed
- 
liftMtoMonadTransinstance methodlift
- 
liftEitherMtoliftEither
- 
Resolver operation m event value->Resolver operation event m value, monad trans needs that last 2 type arguments are monad and value that why it was necessary
- 
exposed Data.Morpheus.Types.Internal.AST
- 
Mutation Resolver was changed from 
resolver :: () -> ResolveM EVENT IO Address
resolver = MutResolver  {
  mutEvents = [someEventForSubscription],
  mutResolver = lift setDBAddress
}
-- Mutation Wit Event Triggering : sends events to subscription
resolver :: () -> ResolveM EVENT IO Address
resolver = MutResolver \$ do
  value <- lift setDBAddress
  pure ([someEventForSubscription], value)
-- or
-- Mutation Without Event Triggering
resolver :: () -> ResolveM EVENT IO Address
resolver _args = lift setDBAddress
Added
- 
added parseDSLtoData.Morpheus.Document
- 
GraphQL SDL support fully supports descriptions: onTypes, fields , args … with (enums, inputObjects , union, object) for example : """ Description for Type Address """ type Address { """ Description for Field city """ city: String! street( """ Description argument id """ id: ID! ): Int! }GraphQL SDLtype User { name: String! @deprecated(reason: "some reason") }will displayed in introspection introspection.json{ "data": { "__type": { "fields": [ { "name": "city", "isDeprecated": true, "deprecationReason": "test deprecation field with reason" } ] } } }
- 
basic support of directive @deprecatedonenumValueand objectfield, only on introspection
- 
GraphQL Client deprecation warnings on type type Human { humanName: String! lifetime: Lifetime! @deprecated(reason: "some reason") profession: Profession }compiler output: warning: Morpheus Client Warning: { "message":"the field \"Human.lifetime\" is deprecated. some reason", "locations":[{"line":24,"column":15}] }
- 
new helper resolver types aliases: - ResolveQ : for Query
- ResolveM : for Mutation
- ResolveS : for Subscription
 ResolveM EVENT IO Addressis same asMutRes EVENT IO (Address (MutRes EVENT IO))is helpful when you want to resolve GraphQL object 
Fixed
- added missing Monad instance for Mutation resolver
- defineByIntrospectionFiledoes not breaks if schema contains interfaces
- Morpheus Client supports SubscriptionandMutationoperations
[0.6.2] - 2.11.2019
Added
- support of ghc 8.8.1
[0.6.0] - 1.11.2019
Removed
- 
removed morpheuscli for code generating, if you need cli you should use morpheus-graphql-cli
- 
example APIexecutable is removed from Production build
Added
- 
helper functions: liftEitherM,liftMliftM :: m a -> Resolver o m e a liftEitherM :: m (Either String a) -> Resolver o m e a
[0.5.0] - 31.10.2019
Added
- dummy support of directives, only parsing not actual implementation
Fixed
- 
can be parsed implementswith multiple interfaces separated by&
- 
can be parsed default value on inputObject
- 
Parser supports anonymous Operation: query,mutation,subscriptionfor example:mutation { name }
- 
Morpheus client does not breaks on Booleantype, converts every GraphQL typeBooleanto haskellBooland GQLStringtoText
Changed
- 
Reduced GQLRootResolversignature :GQLRootResolver IO () () Query () ()->GQLRootResolver IO () Query () ()GQLRootResolver IO Channel Content Query Mutation Subscription->GQLRootResolver IO APIEvent Query Mutation Subscriptionwhere APIEvent = Event Channel Content
- 
GQLRootResolverautomatically assigns corresponding monad to GraphQL Types.you can write just: GQLRootResolver IO APIEvent Query Mutation Subscriptioninstead of: GQLRootResolver IO APIEvent (Query (Resolver IO)) (Mutation (MutResolver IO ApiEvent) (Subscription (SubResolver IO ApiEvent))where operations are generated by importGQLDocumentor have form :data Query m = Query { field1 :: Args -> m Field1, .... }
- 
()was replaced withUndefinedinGQLRootResolverfor empty operationsmutation,subscriptionrootResolver :: GQLRootResolver IO () Query Undefined Undefined
- 
Root Operations Query,Mutation,Subscriptionare passed to root resolvers without boxing inside a monad.
- 
there are only 3 kind of resolvers MutResolver,SubResolver,QueryResolverdefined by GADTResolver
[0.4.0] - 09.10.2019
Changed
- 
support of Default Value: - on query: Parsing Validating and resolving
- on Document: only Parsing
 
- 
‘lens’ is removed from Library, client field collision can be handled with GraphQL alias:{ user { name friend { friendName: name } } }
Fixed:
- 
Data.Morpheus.Document.toGraphQLDocumentgenerates only my user defined types. #259
- 
Morpheus Client Namespaces Input Type Fields, they don’t collide anymore: example: schema: input Person { name: String! }query: query GetUser (parent: Person!) { .... }wil generate: data GetUserArgs = GetUserArgs { getUserArgsParent: Person } deriving ... data Person = Person { personName: Person } deriving ...
- 
Morpheus Client Generated Output Object And Union Types don’t collide: type Person { name: String! parent: Person! friend: Person! }And we select { user { name friend { name } parent { name } bestFriend: friend { name parent { name } } } }client will Generate: - UserPersonfrom- {user
- UserFriendPerson: from- {user{friend
- UserParentPerson: from- {user{parent
- UserBestFriendPerson: from- {user{bestFriend
- UserBestFriendParentPerson: from- {user{bestFriend{parent
 
- 
GraphQL Client Defines enums and Input Types only once per query and they don’t collide 
[0.3.1] - 05.10.2019
Changed
- removed dependencies: attoparsec , utf8-string
- updated aeson lower bound up to: 1.4.4.0
[0.3.0] - 04.10.2019
Added
- 
user can import GraphQL Document and generate types with it. importGQLDocument "API.gql"this will generate types defined in API.gql
Fixed
- 
Stringdefined in GQLDocument will be converted toTextby template haskell
- 
importGQLDocumentandgqlDocumentsupports Mutation, Subscription and Resolvers with custom Monadfor example. if we have: type Deity { name: String! power: Power! }where Poweris another object defined by gql schema. template haskell will represent this type as:data Deity m = Deity { name :: () -> m Text, power :: () -> m (Power m) }where mis resolver Monad.
- 
importGQLDocumentWithNamespacegenerates namespaced haskell records. so that you have no more problem with name collision. from this gql type:type Deity { name: (id:Int)String! power: Power! }will be generated. data Deity m = Deity { deityName :: DeityNameArgs -> m Text, deityPower :: () -> m (Power m) } data DeityNameArgs = DeityNameArgs { deityNameArgsId :: Int }
Changed
- 
GQLTypeis mandatory for every GQL Type (including Query, Mutation and Subscription)
- 
subscription Resolver changed from: Subscription {newDeity = \args -> Event {channels = [ChannelA], content = newDeityResolver } }to: Subscription {newDeity = \args -> SubResolver {subChannels = [ChannelA], subResolver = newDeityResolver } }
[0.2.2] - 30.08.2019
Fixed
- 
Parser Supports GraphQL multiline comments 
- 
Morpheus GraphQL Client: Support GraphQL Alias 
- 
Support of GraphQL Interfaces on GraphQL Document: # simple.gql interface Node { nodeId: ID! } type SimpleType implements Node { nodeId: ID! name: String! }morpheus compiler will read interfaces and validate implements. template haskell will generate haskell types only for types not for interfaces. haskell type from simple.gql:data SimpleType = SimpleType { nodeId :: ID! name :: Text! } deriving (Generic)at the time compiler does not validates field Arguments by interface 
[0.2.1] - 23.08.2019
- assets are added to cabal source files
[0.2.0] - 23.08.2019
Added
- 
Parser Supports GraphQL comments 
- 
Enhanced Subscription: mutation can trigger subscription with arguments 
- 
Experimental Support of Input Unions 
- 
GraphQL schema generating with: Data.Morpheus.Document.toGraphQLDocument
- 
Generating dummy Morpheus Api from schema.gql:morpheus build schema/mythology.gql src/MythologyApi.hs
- 
convertToJSONName&convertToHaskellNamehas been extended to support all Haskell 2010 reserved identities. details
- 
GraphQL Clientwith Template haskell QuasiQuotes (Experimental, Not fully Implemented)defineQuery [gql| query GetHero ($byRealm: Realm) { deity (realm:$byRealm) { power fullName } } |]will Generate: - response type GetHero,DeitywithLensInstances
- input types: GetHeroArgs,Realm
- instance for FetchtypeClass
 so that fetchHero :: Args GetHero -> m (Either String GetHero) fetchHero = fetch jsonRes args where args = GetHeroArgs {byRealm = Just Realm {owner = "Zeus", surface = Just 10}} jsonRes :: ByteString -> m ByteString jsonRes = <fetch query from server>resolves well typed response GetHero.
- response type 
- 
Ability to define GQLSchemawith GraphQL syntax , so that with this schema[gqlDocument| type Query { deity (uid: Text! ) : Deity! } type Deity { name : Text! power : Text } |] rootResolver :: GQLRootResolver IO () () Query () () rootResolver = GQLRootResolver {queryResolver = return Query {deity}, mutationResolver = pure (), subscriptionResolver = pure ()} where deity DeityArgs {uid} = pure Deity {name, power} where name _ = pure "Morpheus" power _ = pure (Just "Shapeshifting")Template Haskell Generates types: Query,Deity,DeityArgs, that can be used byrootResolvergenerated types are not compatible with Mutation,Subscription, they can be used only inQuery, but this issue will be fixed in next release
Fixed:
- Parser supports enums inside input Object
- fulfilled fragment Validation (added: unusedFragment,nameConflict)
- correct decoding of Enums with more than 3 constructor #201
Changed
- 
WebSocket subProtocol changed from graphql-subscriptionstographql-ws
- 
type family KINDis moved into typeClassesGQLType, so you should replacetype instance KIND Deity = OBJECT instance GQLType Deity where description = const "Custom Description for Client Defined User Type" data Deity = Deity { fullName :: Text } deriving (Generic)with instance GQLType Deity where type KIND Deity = OBJECT description = const "Custom Description for Client Defined User Type" data Deity = Deity { fullName :: Text } deriving (Generic)
- 
Duplicated variable names in Http requests are validated using Aeson’sjsonNoDupfunction. So the following request will result in a parsing error{"query":"...", "variables":{"email":"[email protected]", "email":"[email protected]",...}}
[0.1.1] - 1.07.2019
Fixed:
- () as Subscription or Mutation does not defines Operator without fields
[0.1.0] - 30.06.2019
thanks for contributing to: @krisajenkins, @hovind, @vmchale, @msvbg
Added
- 
support for Union Types: type instance KIND <type> = UNION
- 
support of haskell Types: Map,Set, and Pair(a,b)
- 
GraphQL Resolver supports custom Monad 
- 
add Interpreterclass with instances:- ByteString -> m ByteStringand Lazy- ByteString, where- mis resolver monad
- Text -> m Textand Lazy- Text, where- mis resolver monad
- GQLRequest -> m GQLResponse, When you using it inside another Component that have Manual- ToJSONderiving, you have to ensure that- GQLResponsewill be encoded with- toEncoding, and not with- toJSON.
 
- 
Schema Validation: - Name Collision
 
- 
support of Parsing input values: Objects,Arrays
- 
support scalar type: ID
- 
scalar Types are validated by GQLScalarinstance functionparseValue
- 
TypeFamily KINDwith:- SCALAR
- OBJECT,
- ENUM
- INPUT_OBJECT
- UNION
 
- 
inline Fragments 
- 
GraphQL Aliases 
- 
Subscriptions: GQLSubscription- a -> EffectM boperation: is resolver that contains side effect in- EffectM. is used for Mutation and Subscribe communication
- gqlEffectResolver ["CHANNEL_ID"]: packs as effect Resolver. if mutation and subscription resolver have same channel then every call of mutation will trigger subscription resolver
- GQLState: shared state between- httpand- websocketserver
- gqlSocketApp:converts- interpreterto- websocketapplication
- graphql-subscriptions:- Apollo GraphQLsubProtocol
 
- 
language: - Query supports : __type(name:"type")
- On every Object can be selected : __typename
 
- Query supports : 
Changed
- GQLRootResolver,- GQLType(..),- GQLScalar(..)are moved in- Data.Morpheus.Types
- GQLRoot { query, mutation, subscription }to- GQLRootResolver {queryResolver, mutationResolver, subscriptionResolver}
- interpreter: can be used in- httpand- websocketserver
- GQLKindrenamed as- GQLType
- types can be derived just with (Generic,GQLType)
- haskell record field type'will generate GQL Object fieldtype
- public API (all other modules are hidden):
- Data.Morpheus
- Data.Morpheus.Kind
- Data.Morpheus.Types
- Data.Morpheus.Execution.Subscription
 
Fixed:
- parser can read fields with digits like: a1 , _1
- you can use Wrapped type and Wrapped Primitive Types issue #136:
- wrapped TypesNames will be separated with “_” : typeName(Either A B) -> “Either_A_B”
 
- introspection:
- argument supports Non-NullandList
- every field has correct kind
 
- argument supports 
Removed
- GQLArgs: you can derive arguments just with- Genericwithout- GQLArgs
- GQLObject: replaced with instance- type instance KIND <Type> = OBJECT
- GQLEnum: replaced with instance- type instance KIND <Type> = ENUM
- GQLInput: replaced with instance- type instance KIND <Type> = INPUT_OBJECT
- Typeable: with new deriving it is not required anymore
- Wrapper: with TypeFamilies there is no need for- Wrapper
- a ::-> bis Replaced by- a -> ResM bwhere- ResMis alias for- Resolver IO a
- GQLMutation,- GQLQuery: with new deriving it is not required anymore
- Resolverconstructor replaced by functions:- gqlResolver: packs- m Either String ato- Resolver m a
- gqlEffectResolver: resolver constructor for effectedResolver
- liftEffectResolver: lifts normal resolver to Effect Resolver.
 
