MIT licensed by Daviti Nalchevanidze

Module documentation for 0.7.1

This version can be pinned in stack with:morpheus-graphql-0.7.1@sha256:dc8fde135a0a73e9eeb01b6b4e07cd73d8d45def7e8fd7c9586fd24c499e7e1f,21024

Morpheus GraphQL Hackage CircleCI

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.7.1

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!
}

"""
Description for Deity
"""
type Deity {
  """
  Description for name
  """
  name: String!
  power: String String! @deprecated(reason: "some reason for")
}

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

descriptions and deprecations will be displayed in intropsection.

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 } = liftEither $ 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

Mythology Api

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 Character = 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

alt text alt text alt text

Mutations

In addition to queries, Morpheus also supports mutations. The behave just like regular queries and are defined similarly:

newtype Mutation m = Mutation
  { createDeity :: MutArgs -> m Deity
  } deriving (Generic, GQLType)

rootResolver :: GQLRootResolver IO  () Query Mutation Undefined
rootResolver =
  GQLRootResolver
    { queryResolver = Query {...}
    , mutationResolver = Mutation { createDeity }
    , subscriptionResolver = Undefined
    }
    where
      -- Mutation Without Event Triggering
      createDeity :: MutArgs -> ResolveM () IO Deity
      createDeity_args = lift setDBAddress

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

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)

type APIEvent = Event Channel Content

rootResolver :: GQLRootResolver IO APIEvent Query Mutation Subscription
rootResolver = GQLRootResolver
  { queryResolver        = Query { deity }
  , mutationResolver     = Mutation { createDeity }
  , subscriptionResolver = Subscription { newDeity }
  }
 where
  deity _args = fetchDeity
  -- Mutation Without Event Triggering
  createDeity :: () -> ResolveM EVENT IO Address
  createDeity _args = MutResolver \$ do
      value <- lift dbCreateDeity
      pure (
        [Event { channels = [ChannelA], content = ContentA 1 }],
        value
      )
  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 Fetch typeClass
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>

in this case, jsonRes is 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

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.7.1] - 26.11.2019

  • max bound icludes: support-megaparsec-8.0

[0.7.0] - 24.11.2019

Removed

  • toMorpheusHaskellAPi from Data.Morpheus.Document functionality will be migrated in morpheus-graphql-cli

Changed

  • liftM to MonadTrans instance method lift

  • liftEitherM to liftEither

  • 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 parseDSL to Data.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 SDL
    type 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 @deprecated on enumValue and object field, 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 Address is same as MutRes EVENT IO (Address (MutRes EVENT IO))

    is helpfull wenn you want to resolve GraphQL object

Fixed

  • added missing Monad instance for Mutation resolver
  • defineByIntrospectionFile does not breaks if schema contains interfaces
  • Morpheus Client supports Subscription and Mutationoperations

[0.6.2] - 2.11.2019

Added

  • support of ghc 8.8.1

[0.6.0] - 1.11.2019

Removed

  • removed morpheus cli for code generating, if you need cli you should use morpheus-graphql-cli

  • example API executable is removed from Production build

Added

  • helper functions: liftEitherM , liftM

      liftM :: 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 implements with multiple interfaces separated by &

  • can be parsed default value on inputobject

  • Parser supports anonymous Operation: query , mutation , subscription for example:

    mutation {
       name
    }
    
  • Morpheus client does not breaks on Boolean type, converts every GraphQL type Boolean to haskell Bool and GQL String to Text

Changed

  • Reduced GQLRootResolver signature :

    GQLRootResolver IO () () Query () () -> GQLRootResolver IO () Query () ()

    GQLRootResolver IO Channel Content Query Mutation Subscription -> GQLRootResolver IO APIEvent Query Mutation Subscription

    where APIEvent = Event Channel Content

  • GQLRootResolver automatically assigns corresponding monad to GraphQL Types.

    you can write just:

    GQLRootResolver IO APIEvent Query  Mutation Subscription
    

    instead of:

    GQLRootResolver IO APIEvent (Query (Resolver IO))  (Mutation (MutResolver IO ApiEvent) (Subscription (SubResolver IO ApiEvent))
    

    where operations are generated by importGQLDocument or have form :

    data Query m = Query {
      field1 :: Args -> m Field1,
      ....
    }
    
  • () was replaced with Undefined in GQLRootResolver for empty operations mutation, subscription

    rootResolver :: GQLRootResolver IO () Query Undefined Undefined
    
  • Root Operations Query, Mutation, Subscription are passed to root resolvers without boxing inside a monad.

  • there are only 3 kind of resolvers MutResolver, SubResolver , QueryResolver defined by GADT Resolver

[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.toGraphQLDocument generates 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:

    • UserPerson from {user
    • UserFriendPerson: from {user{freind
    • UserParentPerson: from {user{parent
    • UserBestFriendPerson: from {user{bestFrend
    • UserBestFriendParentPerson: 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

  • String defined in GQLDcoument will be converted to Text by template haskell

  • importGQLDocument and gqlDocument supports Mutation, Subscription and Resolvers with custom Monad

    for example. if we have:

    type Deity {
      name: String!
      power: Power!
    }
    

    where Power is 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 m is resolver Monad.

  • importGQLDocumentWithNamespace generates 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

  • GQLType is 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
    

    details

  • convertToJSONName & convertToHaskellName has been extended to support all Haskell 2010 reserved identities. details

  • GraphQL Client with Template haskell QuasiQuotes (Experimental, Not fully Implemented)

    defineQuery
      [gql|
        query GetHero ($byRealm: Realm)
          {
            deity (realm:$byRealm) {
              power
              fullName
            }
          }
      |]
    

    will Generate:

    • response type GetHero, Deity with Lens Instances
    • input types: GetHeroArgs , Realm
    • instance for Fetch typeClass

    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.

  • Ability to define GQLSchema with 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 by rootResolver

    generated types are not compatible with Mutation, Subscription, they can be used only in Query, 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-subscriptions to graphql-ws

  • type familiy KIND is moved into typeClasses GQLType, so you should replace

    type 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’s jsonNoDup function. So the following request will result in a parsing error

    {"query":"...",
    "variables":{"email":"foo@mail.net", "email":"bar@mail.net",...}}
    

[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 Interpreter class with instances:

    • ByteString -> m ByteString and Lazy ByteString, where m is resolver monad
    • Text -> m Text and Lazy Text, where m is resolver monad
    • GQLRequest -> m GQLResponse , When you using it inside another Component that have Manual ToJSON deriving, you have to ensure that GQLResponse will 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 GQLScalar instance function parseValue

  • TypeFamily KIND with:

    • SCALAR
    • OBJECT,
    • ENUM
    • INPUT_OBJECT
    • UNION
  • inline Fragments

  • GraphQL Aliases

  • Subscriptions: GQLSubscription

    • a -> EffectM b operation: 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 http and websocket server
    • gqlSocketApp :converts interpreter to websocket application
    • graphql-subscriptions: Apollo GraphQL subProtocol
  • language:

    • Query supports : __type(name:"type")
    • On every Object can be selected : __typename

Changed

  • GQLRootResolver, GQLType(..) , GQLScalar(..) are moved in Data.Morpheus.Types
  • GQLRoot { query, mutation, subscription } to GQLRootResolver {queryResolver, mutationResolver, subscriptionResolver}
  • interpreter: can be used in http and websocket server
  • GQLKind renamed as GQLType
  • types can be derived just with (Generic,GQLType)
  • haskell record field type' will generate GQL Object field type
  • 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-Null and List
    • every field has correct kind

Removed

  • GQLArgs: you can derive arguments just with Generic without 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 ::-> b is Replaced by a -> ResM b where ResM is alias for Resolver IO a
  • GQLMutation , GQLQuery : with new deriving it is not required anymore
  • Resolver constructor replaced by functions:
    • gqlResolver : packs m Either String a to Resolver m a
    • gqlEffectResolver: resolver constructor for effectedResolver
    • liftEffectResolver: lifts normal resolver to Effect Resolver.
comments powered byDisqus