Skip to content

Commit c11ea27

Browse files
committed
Merge branch 'master' into docs2
2 parents 89f0f12 + 8575d54 commit c11ea27

File tree

17 files changed

+830
-311
lines changed

17 files changed

+830
-311
lines changed

HLint.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import "hint" HLint.Generalise
33

44
ignore "Use fmap"
55
ignore "Redundant do"
6+
ignore "Use =<<"

README.md

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33
[![CircleCI](https://circleci.com/gh/jml/graphql-api.svg?style=shield)](https://circleci.com/gh/jml/graphql-api)
44
[![Documentation Status](https://readthedocs.org/projects/haskell-graphql-api/badge/?version=latest)](http://haskell-graphql-api.readthedocs.io/en/latest/?badge=latest)
55

6-
This library provides type combinators to create a GraphQL schema, and functions to parse and evaluate queries against the schema. It is inspired by [servant](http://haskell-servant.readthedocs.io/en/stable/) but the two projects don't share any code.
6+
`graphql-api` helps you implement a robust [GraphQL](http://graphql.org/) API in Haskell. By the time a query makes it to your handler you are dealing with strong, static types that make sense for your problem domain. All your handlers are normal Haskell functions because we derive their type signature from the schema. If you have used [servant](http://haskell-servant.readthedocs.io/en/stable/), this will sound familiar.
77

8-
More specifically, `graphql-api` helps with implementing a robust GraphQL API in Haskell. By the time a query makes it to your handler you are dealing with strong, static types. All handlers are normal Haskell functions because we derive their type signature from the schema.
8+
The library provides type combinators to create a GraphQL schema, and functions to parse and evaluate queries against the schema.
99

1010
You can find the latest release on [hackage](https://hackage.haskell.org/package/graphql-api).
1111

12-
Note that we're trying to implement the GraphQL standard as best as we can in Haskell. I.e. even if an alternative API or behaviour looks nicer we will defer to the standard.
12+
We implement the [GraphQL specification](https://facebook.github.io/graphql/) as best as we can in Haskell. We figure they know what they're doing. Even if an alternative API or behaviour looks nicer, we will defer to the spec.
1313

14-
## Hello world example
14+
## Example
1515

16-
Below we define a `Hello` Schema that contains a single field, `greeting`, that takes a single, required argument `who`:
16+
Say we have a simple GraphQL schema like:
17+
18+
```graphql
19+
type Hello {
20+
greeting(who: String!): String!
21+
}
22+
```
23+
24+
which defines a single top-level type `Hello` which contains a single field, `greeting`, that takes a single, required argument `who`.
25+
26+
We can define this schema in Haskell and implement a simple handler like so:
1727

1828
```haskell
29+
{-# LANGUAGE OverloadedStrings #-}
30+
{-# LANGUAGE TypeApplications #-}
31+
{-# LANGUAGE TypeOperators #-}
32+
1933
import Data.Text (Text)
2034
import Data.Monoid ((<>))
2135

@@ -33,26 +47,42 @@ run :: Text -> IO Response
3347
run = interpretAnonymousQuery @Hello hello
3448
```
3549

36-
Note that we require GHC 8.0.2 or later for features like the `@Hello` type application, and for certain bug fixes.
50+
We require GHC 8.0.2 or later for features like the `@Hello` type application, and for certain bug fixes.
3751

3852
With the code above we can now run a query:
3953

4054
```haskell
4155
run "{ greeting(who: \"mort\") }"
4256
```
4357

44-
## Current status
58+
Which will produce the following GraphQL response:
59+
60+
```json
61+
{
62+
data: {
63+
greeting: "Hello mort"
64+
}
65+
}
66+
```
67+
68+
## Status
69+
70+
Our current goal is to gather feedback. We have learned a lot about GraphQL in the course of making this library, but we don't know what a good GraphQL library looks like in Haskell. Please [let us know](https://github.com/jml/graphql-api/issues/new) what you think. We won't mind if you file a bug telling us how good the library is.
4571

46-
This first release's goal is to gather feedback. We make **no** guarantees about API stability, or anything at all really.
72+
Because we're still learning, we make **no** guarantees about API stability, or anything at all really.
4773

48-
We are tracking open problem, missing features & wishlist-items in [GitHub's issue tracker](https://github.com/jml/graphql-api/issues).
74+
We are tracking open problems, missing features & wishlist items in [GitHub's issue tracker](https://github.com/jml/graphql-api/issues).
4975

5076
## Roadmap
5177

5278
* Near future:
53-
- Complete lose ends in current implementation & gather feedback.
79+
- Better error messages (this is really important to us)
80+
- Full support for recursive data types
81+
- Close off lose ends in current implementation & gather feedback
5482
* Medium future:
55-
- Implement introspection
83+
- Full schema validation
84+
- Schema introspection
85+
- Stabilize public API
5686
* Long term:
5787
- Derive client implementations from types
5888
- Allow users to implement their own type combinators

graphql-wai/graphql-wai.cabal

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-- This file has been generated from package.yaml by hpack version 0.15.0.
2+
--
3+
-- see: https://github.com/sol/hpack
4+
5+
name: graphql-wai
6+
version: 0.1.0
7+
synopsis: A simple wai adapter
8+
category: Web
9+
homepage: https://github.com/jml/graphql-api#readme
10+
bug-reports: https://github.com/jml/graphql-api/issues
11+
license: Apache
12+
build-type: Simple
13+
cabal-version: >= 1.10
14+
15+
source-repository head
16+
type: git
17+
location: https://github.com/jml/graphql-api
18+
19+
library
20+
hs-source-dirs:
21+
src
22+
default-extensions: NoImplicitPrelude OverloadedStrings RecordWildCards TypeApplications
23+
ghc-options: -Wall -fno-warn-redundant-constraints -Werror
24+
build-depends:
25+
base >= 4.9 && < 5
26+
, protolude
27+
, exceptions
28+
, wai
29+
, http-types
30+
, graphql-api
31+
, aeson
32+
exposed-modules:
33+
GraphQL.Wai
34+
default-language: Haskell2010
35+
36+
test-suite wai-tests
37+
type: exitcode-stdio-1.0
38+
main-is: Tests.hs
39+
hs-source-dirs:
40+
tests
41+
default-extensions: NoImplicitPrelude OverloadedStrings RecordWildCards TypeApplications
42+
ghc-options: -Wall -fno-warn-redundant-constraints -Werror
43+
build-depends:
44+
base >= 4.9 && < 5
45+
, protolude
46+
, exceptions
47+
, wai
48+
, http-types
49+
, graphql-api
50+
, aeson
51+
, wai-extra
52+
, graphql-wai
53+
default-language: Haskell2010

graphql-wai/package.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: graphql-wai
2+
version: 0.1.0
3+
synopsis: A simple wai adapter
4+
license: Apache
5+
github: jml/graphql-api
6+
category: Web
7+
8+
# NB the "redundant constraints" warning is a GHC bug: https://ghc.haskell.org/trac/ghc/ticket/11099
9+
ghc-options: -Wall -fno-warn-redundant-constraints -Werror
10+
default-extensions:
11+
- NoImplicitPrelude
12+
- OverloadedStrings
13+
- RecordWildCards
14+
- TypeApplications
15+
16+
dependencies:
17+
- base >= 4.9 && < 5
18+
- protolude
19+
- exceptions
20+
- wai
21+
- http-types
22+
- graphql-api
23+
- aeson
24+
25+
library:
26+
source-dirs: src
27+
28+
tests:
29+
wai-tests:
30+
main: Tests.hs
31+
source-dirs: tests
32+
dependencies:
33+
- wai-extra
34+
- graphql-wai

graphql-wai/src/GraphQL/Wai.hs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{-# LANGUAGE AllowAmbiguousTypes #-}
2+
{-# LANGUAGE FlexibleContexts #-}
3+
{-# LANGUAGE ScopedTypeVariables #-}
4+
5+
-- | Basic WAI handlers for graphql-api
6+
module GraphQL.Wai
7+
( toApplication
8+
) where
9+
10+
import Protolude
11+
12+
import GraphQL (interpretAnonymousQuery)
13+
import GraphQL.API (HasObjectDefinition)
14+
import GraphQL.Resolver (HasResolver, Handler)
15+
import Network.Wai (Application, queryString, responseLBS)
16+
import GraphQL.Value.ToValue (toValue)
17+
import Network.HTTP.Types.Header (hContentType)
18+
import Network.HTTP.Types.Status (status200, status400)
19+
import qualified Data.Aeson as Aeson
20+
21+
22+
-- | Adapt a GraphQL handler to a WAI application. This is really just
23+
-- to illustrate the mechanism, and not production ready at this point
24+
-- in time.
25+
--
26+
-- If you have a 'Cat' type and a corresponding 'catHandler' then you
27+
-- can use "toApplication @Cat catHandler".
28+
toApplication
29+
:: forall r. (HasResolver IO r, HasObjectDefinition r)
30+
=> Handler IO r -> Application
31+
toApplication handler = app
32+
where
33+
app req respond =
34+
case queryString req of
35+
[("query", Just query)] -> do
36+
r <- interpretAnonymousQuery @r handler (toS query)
37+
let json = Aeson.encode (toValue r)
38+
respond $ responseLBS status200 [(hContentType, "application/json")] json
39+
_ -> respond $ responseLBS status400 [] "Must provide excatly one query GET argument."

graphql-wai/tests/Tests.hs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{-# LANGUAGE DataKinds #-}
2+
module Main where
3+
4+
import Protolude
5+
6+
import Network.Wai.Test
7+
import GraphQL.API
8+
import GraphQL.Wai
9+
import GraphQL.Resolver
10+
11+
type Cat = Object "Cat" '[] '[Field "name" Text]
12+
13+
catHandler :: Handler IO Cat
14+
catHandler = pure (pure "Felix")
15+
16+
test1 :: Session ()
17+
test1 = do
18+
r <- request $ setPath defaultRequest "/?query={ name }"
19+
assertStatus 200 r
20+
assertBody "{\"data\":{\"name\":\"Felix\"}}" r
21+
22+
main :: IO ()
23+
main = do
24+
void $ runSession test1 (toApplication @Cat catHandler)

0 commit comments

Comments
 (0)