-
(issue #543 & #558):
GQLTypeOptions
supports 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 -> String
that generates a custom GraphQL type name. The first argument is aBool
that isTrue
if the type is an input, andFalse
otherwise. The second argument is aString
representing 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:
Deity
andInputDeity
data 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
EncodeWrapper
andDecodeWrapper
type-classes.
-
Map k v
is now represented as just[Pair k v]
-
GQLScalar
was replaced withEncodeScalar
andDecodeScalar
type-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 } | Smartphone
this 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 | Developer
this 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.typeOptions
fromf a -> GQLTypeOptions
tof 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 themOneTwoThree
instead ofOne_Two_Three.
- Haskell
Float
was renamed to custom scalar typeFloat32.
- Haskell
Double
now represents GraphQLFloat
.
- deprecated kinds
INPUT
,ENUM
andOUTPUT
in 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
importGQLDocument
orimportGQLDocumentWithNamespace
is changed
- subscriptions are extracted in
morpheus-graphql-subscriptions
. Event
,httpPubApp
andwebSocketsApp
movedData.Morpheus.Subscriptions
-
Data.Morpheus.Subscriptions
provides:- runPubApp: generalized version of
httpPubApp
- runSubApp: generalized version of
webSocketsApp
- runPubApp: generalized version of
-
New encode and decode instances for
Set
,NonEmpty
,Seq
andVector
Set
andNonEmpty
throw 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.
- parser performance optimization
-
custom operation root types: e.g
RootResolver IO () MyQuery MyMutation Undefined
creates app with:
schema { query: MyQuery mutation: MyMutation }
-
type :
App event m
andderiveApp
app :: App EVENT IO app = runApp (deriveApp root) api :: a -> IO b api = runApp (deriveApp root)
-
App
supports semigroup(schema Stitching
): if whe have two graphql appsmergedApi :: a -> m b mergedApi = runApp (deriveApp root <> deriveApp root2)
-
GQLType
exposestypeOptions
to modify labels:typeOptions :: f a -> GQLTypeOptions
where
GQLTypeOptions { fieldLabelModifier :: String -> String, constructorTagModifier :: String -> String }
-
you can use
GQLType.getDescriptions
to document field or enum Values -
with
importGQLDocumentWithNamespace
now you can use Enums with Colliding Values:enum X { A } enum Y { A }
they will be namespaced to.
XA
andYA
-
importGQLDocumentWithNamespace
they will be namespaced enum Values -
Argument types must have
GQLType
instances -
in
Data.Morpheus.Server
:- removed
subscriptionApp
- changed
webSocketsApp
type toApp e m -> m (ServerApp, e -> m ())
- changed
httpPubApp
type to[e -> m ()] -> App e m -> a -> m b
- removed
-
removed
Stream
fromData.Morpheus.Types
-
removed class
Interpreter
,interpreter
is now just regular function.interpreter = runApp . deriveApp
- internal refactoring
- 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.Document
exposesRootResolverConstraint
-
Data.Morpheus.Server
exposeshttpPlayground
-
httpPubApp
supportsGQLRequest -> GQLResponse
-
morpheus-graphql-core
support ofschema
. issue #412schema { query: Query }
note that this does not affect
morpheus-graphql-server
at 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.
Context' renamed to
ResolverContext'- 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
subscribe
is 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
- renamed
GQLRootResolver
->RootResolver
importGQLDocument
automatically definesGQLType
instances for scalar definitions- supports default values
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
: useResolverQ
MutRes
,IOMutRes
,ResolveM
: useResolverM
SubRes
,IOSubRes
,ResolveS
: useResolverS
failRes
: useMonadFail
Semigroup
support for ResolverMonadFail
Support for Resolver- flexible resolvers:
ResolverO
,ResolverQ
,ResolverM
,ResolverS
they 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
. extendsResolverO
with 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
importGQLDocument
andDSL
(compile time validation):
-
support default directives:
@skip
and@include
-
SelectionTree interface
- fixed subscription sessions, starting new session does not affects old ones.
- added tests for subscriptions
-
Client generated enum data constructors are now prefixed with with the type name to avoid name conflicts.
-
for Variant selection inputUnion uses
inputname
instead of__typename
-
in
Data.Morpheus.Server
gqlSocketApp
andgqlSocketMonadIOApp
are replaced withwebSocketsApp
- removed
initGQLState
,GQLState
-
for better control of subscriptions
- replaced instance
interpreter gqlRoot state
withinterpreter 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 httpApp
where
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).
- 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
FromJSON
ToJSON
instances forID
- changes to internal types
- fixed validation of apollo websockets requests
-
all constructors of
Resolver
:QueryResolver
,MutResolver
,SubResolver
are unexposed. uselift
,publish
orsubscribe
. 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)
-
exposed
publish
for mutation resolvers, now you can writeresolveCreateUser :: ResolveM EVENT IO User resolveCreateUser = do requireAuthorized publish [userUpdate] liftEither setDBUser
-
exposed
subscribe
for 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 SubField
will convert your subscription monad to query monad.SubField (Resolver Subscription Event IO) User
will 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 }
-
unsafeInternalContext
to 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)
- monad instance for resolvers. thanks @dandoh
- example using stm, authentication, monad transformers. thanks @dandoh
- added dependency
mtl
- removed dependency
mtl
WithOperation
constraint for Generic Resolvers (#347) thanks @dandoh
-
liftEither support in MutResolver (#351)
-
selection of
__typename
on 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`
-
support of resolver fields
m type
for the fields without argumentsdata Deity m = Deity { name :: m Text } -- is equal to data Deity m = Deity { name :: () -> m Text }
-
template haskell generates
m type
instead of() -> m type
for 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) }
-
deprecated:
INPUT_OBJECT
,OBJECT
,UNION
,- use
INPUT
instead ofINPUT_OBJECT
- use
deriving(GQLType)
instead ofOBJECT
orUNION
- 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
Deity
is Object. will generateunion CHaracter = Deity | SomeDeity type SomeDeity { _0: Deity }
failRes
for 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:
CharacterDeity
whereCharacter
is TypeConstructor andDeity
referenced 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 typeCreature
will 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 inCharacterEnumObject
and 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 kind: INPUT_UNION
- 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
- max bound includes: support-megaparsec-8.0
toMorpheusHaskellAPi
fromData.Morpheus.Document
functionality will be migrated inmorpheus-graphql-cli
-
liftM
toMonadTrans
instance methodlift
-
liftEitherM
toliftEither
-
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
parseDSL
toData.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! }
type User { name: String! @deprecated(reason: "some reason") }
will displayed in introspection
{ "data": { "__type": { "fields": [ { "name": "city", "isDeprecated": true, "deprecationReason": "test deprecation field with reason" } ] } } }
-
basic support of directive
@deprecated
onenumValue
and 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 Address
is same asMutRes EVENT IO (Address (MutRes EVENT IO))
is helpful when you want to resolve GraphQL object
- added missing Monad instance for Mutation resolver
defineByIntrospectionFile
does not breaks if schema contains interfaces- Morpheus Client supports
Subscription
andMutation
operations
- support of ghc 8.8.1
-
removed
morpheus
cli for code generating, if you need cli you should use morpheus-graphql-cli -
example
API
executable is removed from Production build
-
helper functions:
liftEitherM
,liftM
liftM :: m a -> Resolver o m e a liftEitherM :: m (Either String a) -> Resolver o m e a
- dummy support of
directives
, only parsing not actual implementation
-
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 typeBoolean
to haskellBool
and GQLString
toText
-
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 withUndefined
inGQLRootResolver
for empty operationsmutation
,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 GADTResolver
-
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 } } }
-
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{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
- removed dependencies: attoparsec , utf8-string
- updated aeson lower bound up to: 1.4.4.0
-
user can import GraphQL Document and generate types with it.
importGQLDocument "API.gql"
this will generate types defined in
API.gql
-
String
defined in GQLDocument will be converted toText
by template haskell -
importGQLDocument
andgqlDocument
supports Mutation, Subscription and Resolvers with custom Monadfor 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 }
-
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 } }
-
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
- assets are added to cabal source files
-
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
&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
withLens
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
. - response type
-
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 byrootResolver
generated types are not compatible with
Mutation
,Subscription
, they can be used only inQuery
, but this issue will be fixed in next release
- Parser supports enums inside input Object
- fulfilled fragment Validation (added: unusedFragment,nameConflict)
- correct decoding of Enums with more than 3 constructor #201
-
WebSocket subProtocol changed from
graphql-subscriptions
tographql-ws
-
type family
KIND
is 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
'sjsonNoDup
function. So the following request will result in a parsing error{"query":"...", "variables":{"email":"[email protected]", "email":"[email protected]",...}}
- () as Subscription or Mutation does not defines Operator without fields
thanks for contributing to: @krisajenkins, @hovind, @vmchale, @msvbg
-
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 LazyByteString
, wherem
is resolver monadText -> m Text
and LazyText
, wherem
is resolver monadGQLRequest -> m GQLResponse
, When you using it inside another Component that have ManualToJSON
deriving, you have to ensure thatGQLResponse
will 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
GQLScalar
instance functionparseValue
-
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 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 betweenhttp
andwebsocket
servergqlSocketApp
:convertsinterpreter
towebsocket
applicationgraphql-subscriptions
:Apollo GraphQL
subProtocol
-
language:
- Query supports :
__type(name:"type")
- On every Object can be selected :
__typename
- Query supports :
GQLRootResolver
,GQLType(..)
,GQLScalar(..)
are moved inData.Morpheus.Types
GQLRoot { query, mutation, subscription }
toGQLRootResolver {queryResolver, mutationResolver, subscriptionResolver}
interpreter
: can be used inhttp
andwebsocket
serverGQLKind
renamed 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
- 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
andList
- every field has correct kind
- argument supports
GQLArgs
: you can derive arguments just withGeneric
withoutGQLArgs
GQLObject
: replaced with instancetype instance KIND <Type> = OBJECT
GQLEnum
: replaced with instancetype instance KIND <Type> = ENUM
GQLInput
: replaced with instancetype instance KIND <Type> = INPUT_OBJECT
Typeable
: with new deriving it is not required anymoreWrapper
: with TypeFamilies there is no need forWrapper
a ::-> b
is Replaced bya -> ResM b
whereResM
is alias forResolver IO a
GQLMutation
,GQLQuery
: with new deriving it is not required anymoreResolver
constructor replaced by functions:gqlResolver
: packsm Either String a
toResolver m a
gqlEffectResolver
: resolver constructor for effectedResolverliftEffectResolver
: lifts normal resolver to Effect Resolver.