forked from OSS/graphql
177 lines
4.0 KiB
ReStructuredText
177 lines
4.0 KiB
ReStructuredText
|
========================
|
||
|
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.
|