Skip to content

Lexing, parsing, pretty-printing, and metaprogramming facilities for dealing with GraphQL schemas and queries

License

Notifications You must be signed in to change notification settings

dmjio/graphql-meta

Repository files navigation

graphql-meta

A GraphQL toolkit providing the following:

  • Alex Lexer of the GraphQL lexical specification.
  • Happy Parser of the GraphQL BNF Grammar.
  • Pretty printer of the GraphQL abstract syntax tree (AST) for human consumption.
  • QuickCheck generators for creating random AST fragments
    • Used in conjunction with pretty printing to establish round trip property tests.
    • Source
  • Generics implementation providing correct-by-construction Schema at compile time.
  • QuasiQuoter providing inline definitions of ExecutableDefinitions.

Table of Contents

Query

{-# LANGUAGE QuasiQuotes #-}

module Main (main) where

import GraphQL.QQ (query)

main :: IO ()
main = print [query| { building (id: 123) {floorCount, id}} |]

Result

QueryDocument {getDefinitions = [
  DefinitionOperation (AnonymousQuery [
	SelectionField (Field Nothing (Name {unName = "building"}) [
	  Argument (Name {unName = "id"}) (ValueInt 123)] [] [
	SelectionField (Field Nothing (Name {unName = "floorCount"}) [] [] [])
	  , SelectionField (Field Nothing (Name {unName = "id"}) [] [] [])
	  ])
	])
  ]}

Substitution

GraphQL ExecutableDefinition abstract syntax tree rewriting is made possible via Template Haskell's metavariable substitution. During QuasiQuotation all unbound variables in a GraphQL query that have identical names inside the current scope will automatically be translated into GraphQL AST terms and substituted.

buildingQuery
  :: Int
  -> ExecutableDefinition
buildingQuery buildingId =
  [query| { building (id: $buildingId) {floorCount, id}} |]

Result

QueryDocument {getDefinitions = [
  DefinitionOperation (AnonymousQuery [
	SelectionField (Field Nothing (Name {unName = "building"}) [
	  Argument (Name {unName = "buildingId"}) (ValueInt 4)] [] [
	SelectionField (Field Nothing (Name {unName = "floorCount"}) [] [] [])
	  , SelectionField (Field Nothing (Name {unName = "id"}) [] [] [])
	  ])
	])
  ]}

Generics

It is possible to derive GraphQL schema using GHC.Generics. Simply import GHC.Generics, derive Generic (must enable the DeriveGeneric language extension) and make an instance of ToObjectTypeDefintion. See below for an example:

{-# LANGUAGE DeriveGeneric #-}

module Main where

import           GHC.Generics                    (Generic)
import           GraphQL.Internal.Syntax.Encoder (schemaDocument)
import           Data.Proxy                      (Proxy)
import qualified Data.Text.IO                    as T
import           GraphQL.Generic                 (ToObjectTypeDefinition(..))

data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Show, Eq, Generic)

instance ToObjectTypeDefinition Person

showPersonSchema :: IO ()
showPersonSchema = print $ toObjectTypeDefinition (Proxy @ Person)

-- type Person{name:String!,age:Int!}

Limitations

  • Generic deriving is currently only supported on product types with record field selectors.
  • Only ObjectTypeDefintion is currently supported.

Roadmap

  • Generic deriving of ScalarTypeDefintion and EnumTypeDefintion.

Maintainers

Credit

License

BSD3 2018-2019 Urbint Inc.

About

Lexing, parsing, pretty-printing, and metaprogramming facilities for dealing with GraphQL schemas and queries

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published