summaryrefslogtreecommitdiff
path: root/README.md
blob: 065645a6b914afb1f9f9a0336e42699e0abc3042 (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
# GraphQL implementation in Haskell.

[![Hackage Version](https://img.shields.io/hackage/v/graphql.svg)](https://hackage.haskell.org/package/graphql)
[![Build Status](https://github.com/caraus-ecms/graphql/workflows/Haskell%20CI/badge.svg)](https://github.com/caraus-ecms/graphql/actions?query=workflow%3A%22Haskell+CI%22)
[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/graphql/master/LICENSE)
[![Simple Haskell](https://www.simplehaskell.org/badges/badge.svg)](https://www.simplehaskell.org)

This implementation is relatively low-level by design, it doesn't provide any
mappings between the GraphQL types and Haskell's type system and avoids
compile-time magic. It focuses on flexibility instead, so other solutions can
be built on top of it.

## State of the work

For now this library provides:

- Parser for the query and schema languages, as well as a printer for the query
  language (minimizer and pretty-printer).
- Data structures to define a type system.
- Executor (queries, mutations and subscriptions are supported).
- Validation is work in progress.
- Introspection isn't available yet.

But the idea is to be a Haskell port of
[`graphql-js`](https://github.com/graphql/graphql-js).

For a more precise list of currently missing features see issues marked as
"[not implemented](https://github.com/caraus-ecms/graphql/labels/not%20implemented)".

## Documentation

API documentation is available through
[Hackage](https://hackage.haskell.org/package/graphql).

You'll also find a small tutorial with some examples under
[docs/tutorial](https://github.com/caraus-ecms/graphql/tree/master/docs/tutorial).

### Getting started

We start with a simple GraphQL API that provides us with some famous and less
famous cites.

```graphql
"""
Root Query type.
"""
type Query {
  """
  Provides a cite.
  """
  cite: String!
}
```

This is called a GraphQL schema, it defines all queries supported by the API.
`Query` is the root query type. Every GraphQL API should define a query type.

`Query` has a single field `cite` that returns a `String`. The `!` after the
type denotes that the returned value cannot be `Null`. GraphQL fields are
nullable by default.

To be able to work with this schema, we are going to implement it in Haskell.

```haskell
{-# LANGUAGE OverloadedStrings #-}

import Control.Exception (SomeException)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as ByteString.Lazy.Char8
import qualified Data.HashMap.Strict as HashMap
import Language.GraphQL
import Language.GraphQL.Type
import qualified Language.GraphQL.Type.Out as Out

-- GraphQL supports 3 kinds of operations: queries, mutations and subscriptions.
-- Our first schema supports only queries.
schema :: Schema IO
schema = Schema
    { query = queryType, mutation = Nothing, subscription = Nothing }

-- GraphQL distinguishes between input and output types. Input types are field
-- argument types and they are defined in Language.GraphQL.Type.In. Output types
-- are result types, they are defined in Language.GraphQL.Type.Out. Root types
-- are always object types.
--
-- Here we define a type "Query". The second argument is an optional
-- description, the third one is the list of interfaces implemented by the
-- object type. The last argument is a field map. Keys are field names, values
-- are field definitions and resolvers. Resolvers are the functions, where the
-- actual logic lives, they return values for the respective fields.
queryType :: Out.ObjectType IO
queryType = Out.ObjectType "Query" (Just "Root Query type.") []
    $ HashMap.singleton "cite" citeResolver
  where
    -- 'ValueResolver' is a 'Resolver' data constructor, it combines a field
    -- definition with its resolver function. This function resolves a value for
    -- a field (as opposed to the 'EventStreamResolver' used by subscriptions).
    -- Our resolver just returns a constant value.
    citeResolver = ValueResolver citeField
        $ pure "Piscis primum a capite foetat"
    -- The first argument is an optional field description. The second one is
    -- the field type and the third one is for arguments (we have none in this
    -- example).
    --
    -- GraphQL has named and wrapping types. String is a scalar, named type.
    -- Named types are nullable by default. To make our "cite" field
    -- non-nullable, we wrap it in the wrapping type, Non-Null.
    citeField = Out.Field
        (Just "Provides a cite.") (Out.NonNullScalarType string) HashMap.empty

-- Now we can execute a query. Since our schema defines only one field,
-- everything we can do is to ask to resolve it and give back the result.
-- Since subscriptions don't return plain values, the 'graphql' function returns
-- an 'Either'. 'Left' is for subscriptions, 'Right' is for queries and
-- mutations.
main :: IO ()
main = do
    Right result <- graphql schema "{ cite }"
    ByteString.Lazy.Char8.putStrLn $ Aeson.encode result
```

Executing this query produces the following JSON:

```json
{
  "data": {
    "cite": "Piscis primum a capite foetat"
  }
}
```

## Further information

- [Contributing guidelines](CONTRIBUTING.md).
- [Changelog](CHANGELOG.md) – this one contains the most recent changes; 
  individual changelogs for specific versions can be found on
  [Hackage](https://hackage.haskell.org/package/graphql).

## Contact

Suggestions, contributions and bug reports are welcome.

Should you have questions on usage, please open an issue and ask – this helps
to write useful documentation.

Feel free to contact on Slack in [#haskell on
GraphQL](https://graphql.slack.com/messages/haskell/). You can obtain an
invitation [here](https://graphql-slack.herokuapp.com/).