======================== GraphQL Haskell Tutorial ======================== .. contents:: :depth: 3 .. Getting started =============== Welcome to graphql-haskell! We have written a small tutorial to help you (and ourselves) understand the graphql package. Since this file is a literate haskell file, we start by importing some dependencies. .. code:: haskell {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} module Main where import Prelude hiding (empty, putStrLn) import Data.GraphQL import Data.GraphQL.Schema import qualified Data.GraphQL.Schema as Schema import Control.Applicative import Data.List.NonEmpty (NonEmpty((:|))) import Data.Text hiding (empty) import Data.Aeson import Data.ByteString.Lazy.Char8 (putStrLn) import Data.Time import Debug.Trace First example ------------- Now, as our first example, we are going to look at the example from `graphql.js `__. First we build a GraphQL schema. .. code:: haskell schema1 :: Alternative f => Schema f schema1 = hello :| [] hello :: Alternative f => Resolver f hello = Schema.scalar "hello" ("it's me" :: Text) This defines a simple schema with one type and one field, that resolves to a fixed value. Next we define our query. .. code:: haskell query1 :: Text query1 = "{ hello }" To run the query, we call the ``graphql`` with the schema and the query. .. code:: haskell main1 :: IO () main1 = putStrLn =<< encode <$> graphql schema1 query1 This runs the query by fetching the one field defined, returning ``{"data" : {"hello":"it's me"}}`` Monadic actions --------------- For this example, we're going to be using time. .. code:: haskell schema2 :: Schema IO schema2 = time :| [] time :: Resolver IO time = Schema.scalarA "time" $ \case [] -> do t <- getCurrentTime return $ show t _ -> empty This defines a simple schema with one type and one field, which resolves to the current time. Next we define our query. .. code:: haskell query2 :: Text query2 = "{ time }" main2 :: IO () main2 = putStrLn =<< encode <$> graphql schema2 query2 This runs the query, returning the current time ``{"data": {"time":"2016-03-08 23:28:14.546899 UTC"}}`` Errors ------ Errors are handled according to the spec, with fields that cause erros being resolved to ``null``, and an error being added to the error list. An example of this is the following query: .. code:: haskell queryShouldFail :: Text queryShouldFail = "{ boyhowdy }" Since there is no ``boyhowdy`` field in our schema, it will not resolve, and the query will fail, as we can see in the following example. .. code:: haskell mainShouldFail :: IO () mainShouldFail = do r <- graphql schema1 query1 putStrLn $ encode r putStrLn "This will fail" r <- graphql schema1 queryShouldFail putStrLn $ encode r This outputs: :: {"data": {"hello": "it's me"}} This will fail {"data": {"boyhowdy": null}, "errors":[{"message": "the field boyhowdy did not resolve."}]} Combining resolvers ------------------- Now that we have two resolvers, we can define a schema which uses them both. .. code:: haskell schema3 :: Schema IO schema3 = hello :| [time] query3 :: Text query3 = "query timeAndHello { time hello }" main3 :: IO () main3 = putStrLn =<< encode <$> graphql schema3 query3 This queries for both time and hello, returning ``{ "data": {"hello":"it's me","time":"2016-03-08 23:29:11.62108 UTC"}}`` Notice that we can name our queries, as we did with ``timeAndHello``. Since we have only been using single queries, we can use the shorthand ``{ time hello}``, as we have been doing in the previous examples. In GraphQL there can only be one operation per query. Further examples ================ More examples on queries and a more complex schema can be found in the test directory, in the `Test.StarWars <../../tests/Test/StarWars>`__ module. This includes a more complex schema, and more complex queries.