morpheus-graphql
Morpheus GraphQL
| Version on this page: | 0.6.2 |
| LTS Haskell 23.28: | 0.28.1 |
| Stackage Nightly 2025-05-05: | 0.28.1 |
| Latest on Hackage: | 0.28.1 |
morpheus-graphql-0.6.2@sha256:10f85311c04af4a67526d36e899d154ff5dd01ff619282f024b44f6dd63e0817,20460Module documentation for 0.6.2
Morpheus GraphQL

Build GraphQL APIs with your favourite 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. Mopheus 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-14.8
extra-deps:
- morpheus-graphql-0.6.2
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!): Deity!
}
type Deity {
name: String!
power: String
}
API.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module API (api) where
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Morpheus (interpreter)
import Data.Morpheus.Document (importGQLDocumentWithNamespace)
import Data.Morpheus.Types (GQLRootResolver (..), IORes)
import Data.Text (Text)
importGQLDocumentWithNamespace "schema.gql"
rootResolver :: GQLRootResolver IO () Query Undefined Undefined
rootResolver =
GQLRootResolver
{
queryResolver = Query {queryDeity},
mutationResolver = Undefined,
subscriptionResolver = Undefined
}
where
queryDeity QueryDeityArgs {queryDeityArgsName} = pure Deity {deityName, deityPower}
where
deityName _ = pure "Morpheus"
deityPower _ = pure (Just "Shapeshifting")
api :: ByteString -> IO ByteString
api = interpreter rootResolver
Template Haskell Generates types: Query , Deity, DeityArgs, that can be used by rootResolver
importGQLDocumentWithNamespace will generate Types with namespaced fields. if you don’t need napespacing 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 -> IORes 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)
instance GQLType Deity where
type KIND Deity = OBJECT
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 -> IORes e Deity
resolveDeity DeityArgs { name, mythology } = liftEitherM $ dbDeity name mythology
askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB = ...
Note that the type a -> IORes b is just Synonym for a -> ExceptT String IO b
To make this Query type available as an API, we define a GQLRootResolver and feed it to the Morpheus interpreter. A GQLRootResolver consists of query, mutation and subscription definitions, while we omit the latter for this example:
rootResolver :: GQLRootResolver IO () Query Undefined Undefined
rootResolver =
GQLRootResolver
{ 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
= DEITY Deity
| HUMAN Human
deriving (Generic)
instance GQLType Character where
type KIND City = UNION
Scalar types
To use custom scalar types, you need to provide implementations for parseValue and serialize respectively.
data Odd = Odd Int deriving (Generic)
instance GQLScalar Odd where
parseValue (Int x) = pure $ Odd (... )
parseValue (String x) = pure $ Odd (... )
serialize (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

Mutations
In addition to queries, Morpheus also supports mutations. The behave just like regular queries and are defined similarly:
Just exchange deriving GQLQuery for GQLMutation and declare them separately at the GQLRootResolver definition
newtype Mutation m = Mutation
{ createDeity :: Form -> m Deity
} deriving (Generic, GQLType)
createDeityMutation :: Form -> m (Deity m)
createDeityMutation = ...
rootResolver :: GQLRootResolver IO Query Mutation Undefined
rootResolver =
GQLRootResolver
{ queryResolver = Query {...}
, mutationResolver = Mutation {
createDeity = createDeityMutation
}
, subscriptionResolver = Undefined
}
gqlApi :: ByteString -> IO ByteString
gqlApi = interpreter rootResolver
Subscriptions
im morpheus subscription and mutation communicating with Events,
Event consists with user defined Channel and Content.
every subscription has own Channel by which will be triggered
data Channel
= ChannelA
| ChannelB
data Content
= ContentA Int
| ContentB Text
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)
type APIEvent = Event Channel Content
rootResolver :: GQLRootResolver IO APIEvent Query Mutation Subscription
rootResolver =
GQLRootResolver
{ queryResolver = Query {deity = const fetchDeity}
, mutationResolver = Mutation {createDeity}
, subscriptionResolver = Subscription {newDeity}
}
where
createDeity _args = MutResolver events updateDeity
where
events = [Event {channels = [ChannelA], content = ContentA 1}]
updateDeity = updateDBDeity
newDeity _args = SubResolver [ChannelA] subResolver
where
subResolver (Event [ChannelA] (ContentA _value)) = fetchDeity -- resolve New State
subResolver (Event [ChannelA] (ContentB _value)) = fetchDeity -- resolve New State
subResolver _ = fetchDeity -- Resolve Old State
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
}
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
}
-- from: {deity{worships
data DeityWorshipsDeity = DeityWorshipsDeity {
name: Text,
}
data GetHeroArgs = GetHeroArgs {
getHeroArgsCharacter: Character
}
data Character = Character {
characterName: 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 {getHeroArgsCharacter = Person {characterName = "Zeus"}}
jsonRes :: ByteString -> m ByteString
jsonRes = <GraphQL APi>
types can be generatet from introspection too:
defineByIntrospectionFile "./introspection.json"
Morpheus CLI for Code Generating
you should use morpheus-graphql-cli
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
[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{userUserFriendPerson: from{user{freindUserParentPerson: from{user{parentUserBestFriendPerson: from{user{bestFrendUserBestFriendParentPerson: from{user{bestFrend{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 GQLDcoument 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 familiy
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 LazyByteString, wheremis resolver monadText -> m Textand LazyText, wheremis resolver monadGQLRequest -> m GQLResponse, When you using it inside another Component that have ManualToJSONderiving, you have to ensure thatGQLResponsewill be encoded withtoEncoding, and not withtoJSON.
-
Schema Validation:
- Name Collision
-
support of Parsing input values:
Objects,Arrays -
support scalar type:
ID -
scalar Types are validated by
GQLScalarinstance functionparseValue -
TypeFamily
KINDwith:SCALAROBJECT,ENUMINPUT_OBJECTUNION
-
inline Fragments
-
GraphQL Aliases
-
Subscriptions:
GQLSubscriptiona -> EffectM boperation: is resolver that contains side effect inEffectM. is used for Mutation and Subscribe communicationgqlEffectResolver ["CHANNEL_ID"]: packs as effect Resolver. if mutation and subscription resolver have same channel then every call of mutation will trigger subscription resolverGQLState: shared state betweenhttpandwebsocketservergqlSocketApp:convertsinterpretertowebsocketapplicationgraphql-subscriptions:Apollo GraphQLsubProtocol
-
language:
- Query supports :
__type(name:"type") - On every Object can be selected :
__typename
- Query supports :
Changed
GQLRootResolver,GQLType(..),GQLScalar(..)are moved inData.Morpheus.TypesGQLRoot { query, mutation, subscription }toGQLRootResolver {queryResolver, mutationResolver, subscriptionResolver}interpreter: can be used inhttpandwebsocketserverGQLKindrenamed asGQLType- 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 withGenericwithoutGQLArgsGQLObject: replaced with instancetype instance KIND <Type> = OBJECTGQLEnum: replaced with instancetype instance KIND <Type> = ENUMGQLInput: replaced with instancetype instance KIND <Type> = INPUT_OBJECTTypeable: with new deriving it is not required anymoreWrapper: with TypeFamilies there is no need forWrappera ::-> bis Replaced bya -> ResM bwhereResMis alias forResolver IO aGQLMutation,GQLQuery: with new deriving it is not required anymoreResolverconstructor replaced by functions:gqlResolver: packsm Either String atoResolver m agqlEffectResolver: resolver constructor for effectedResolverliftEffectResolver: lifts normal resolver to Effect Resolver.