Added a tutorial, based on graphql-js and servant documentation.
This commit is contained in:
		
							
								
								
									
										4
									
								
								docs/tutorial/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/tutorial/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
default:
 | 
			
		||||
	pandoc -f markdown+lhs+yaml_metadata_block --highlight-style=haddock -S -c "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" --section-divs -c tutorial.css --toc --standalone -t html5 -o tutorial.html tutorial.lhs
 | 
			
		||||
	pandoc -f markdown+lhs+yaml_metadata_block --highlight-style=haddock --toc --standalone -t rst -o tutorial.rst tutorial.lhs
 | 
			
		||||
	pandoc -f markdown+lhs+yaml_metadata_block --highlight-style=haddock --toc --standalone -t latex -o tutorial.pdf tutorial.lhs
 | 
			
		||||
							
								
								
									
										3
									
								
								docs/tutorial/tutorial.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/tutorial/tutorial.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
body {
 | 
			
		||||
    padding: 0 20px;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								docs/tutorial/tutorial.lhs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								docs/tutorial/tutorial.lhs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
---
 | 
			
		||||
title: GraphQL Haskell Tutorial
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== 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.
 | 
			
		||||
 | 
			
		||||
> {-# 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.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](https://github.com/graphql/graphql-js).
 | 
			
		||||
 | 
			
		||||
First we build a GraphQL schema.
 | 
			
		||||
 | 
			
		||||
> schema1 :: Alternative f => Schema f
 | 
			
		||||
> schema1 = Schema [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.
 | 
			
		||||
 | 
			
		||||
> query1 :: Text
 | 
			
		||||
> query1 = "{ hello }"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
To run the query, we call the `graphql` with the schema and the query.
 | 
			
		||||
 | 
			
		||||
> 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.
 | 
			
		||||
 | 
			
		||||
> schema2 :: Schema IO
 | 
			
		||||
> schema2 = Schema [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.
 | 
			
		||||
 | 
			
		||||
> 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:
 | 
			
		||||
 | 
			
		||||
> 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.
 | 
			
		||||
 | 
			
		||||
> 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.
 | 
			
		||||
 | 
			
		||||
> schema3 :: Schema IO
 | 
			
		||||
> schema3 = Schema [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.
 | 
			
		||||
							
								
								
									
										176
									
								
								docs/tutorial/tutorial.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								docs/tutorial/tutorial.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
========================
 | 
			
		||||
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.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 <https://github.com/graphql/graphql-js>`__.
 | 
			
		||||
 | 
			
		||||
First we build a GraphQL schema.
 | 
			
		||||
 | 
			
		||||
.. code:: haskell
 | 
			
		||||
 | 
			
		||||
    schema1 :: Alternative f => Schema f
 | 
			
		||||
    schema1 = Schema [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 = Schema [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 = Schema [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.
 | 
			
		||||
		Reference in New Issue
	
	Block a user