summaryrefslogtreecommitdiff
path: root/docs/tutorial/tutorial.rst
blob: 48fc29f080a3891963c11ce7844d66ec748bb220 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
========================
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 <https://github.com/graphql/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.