graphql-client: A client for Haskell programs to query a GraphQL API

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

A client for Haskell programs to query a GraphQL API.


[Skip to Readme]

Properties

Versions 1.0.0, 1.1.0, 1.1.0, 1.1.1
Change log CHANGELOG.md
Dependencies aeson (>=1.2.4.0 && <1.6), aeson-schemas (==1.3.*), base (>=4.9 && <5), bytestring (>=0.10.8.2 && <0.11), file-embed (>=0.0.10.1 && <0.0.14), graphql-client, http-client (>=0.5.13.1 && <0.8), http-client-tls (>=0.3.5.3 && <0.4), http-types (>=0.12.1 && <0.13), mtl (>=2.2.2 && <2.3), optparse-applicative (>=0.14.2.0 && <0.15.2), path (>=0.6.1 && <0.8.0), path-io (>=1.3.3 && <1.7.0), template-haskell (>=2.12.0.0 && <3), text (>=1.2.3.0 && <1.3), transformers (>=0.5.2.0 && <0.6), typed-process (>=0.2.3.0 && <0.2.7), unliftio-core (>=0.1.1.0 && <0.3) [details]
License BSD-3-Clause
Author Brandon Chinn <brandon@leapyear.io>
Maintainer Brandon Chinn <brandon@leapyear.io>
Category Graphql
Home page https://github.com/LeapYear/graphql-client#readme
Bug tracker https://github.com/LeapYear/graphql-client/issues
Source repo head: git clone https://github.com/LeapYear/graphql-client
Uploaded by leapyear at 2020-09-25T05:06:27Z

Modules

[Index]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees


Readme for graphql-client-1.1.0

[back to package description]

graphql-client

CircleCI Hackage codecov

A client for Haskell applications to query GraphQL APIs. This package provides two resources:

  1. A graphql-codegen executable that can generate Haskell definitions from input .graphql files

  2. The graphql-client Haskell library providing a runQuery function that takes in a query type generated by graphql-codegen

Quickstart

Pre-requisites: Have Node.js installed.

  1. Add graphql-client as a dependency to your package.yaml or Cabal file

  2. stack build --only-dependencies

  3. Write the .graphql queries you wish to use.

  4. Write an appropriate codegen.yml configuration. It should look something like:

    schema: https://example.com/graphql
    documents: path/to/files/*.graphql
    
    hsSourceDir: src/
    apiModule: Example.GraphQL.API
    enumsModule: Example.GraphQL.Enums
    scalarsModule: Example.GraphQL.Scalars
    

    See the "Configuration" section for the full format of this file.

  5. Write the module specified in scalarsModule (e.g. src/Example/GraphQL/Scalars.hs). See the "Configuration" section for more details.

  6. stack exec graphql-codegen

  7. The API module (e.g. src/Example/GraphQL/API.hs) should have been generated with the Haskell definitions needed to run your GraphQL queries. If any of your GraphQL queries use enums, corresponding modules will also be generated (see the "Configuration" section for more details).

The generated API creates a data type for each GraphQL query of the form {queryName}Query (or {queryName}Mutation for mutations). For example, the following GraphQL query would generate the following Haskell code:

query getRecordings($query: String!, $first: Int) {
  search {
    recordings(query: $query, first: $first) {
      nodes {
        title
      }
    }
  }
}
data GetRecordingsQuery = GetRecordingsQuery
  { _query :: Text
  , _first :: Maybe Int
  }

type GetRecordingsSchema = [schema|
  {
    search: Maybe {
      recordings: Maybe {
        nodes: Maybe List Maybe {
          title: Maybe Text,
        },
      },
    },
  }
|]

Data.GraphQL exports a function runQuery which takes in one of the Query or Mutation data types and returns the response, throwing an error if the GraphQL server returns an error.

A full example of the API in action:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}

import Control.Monad.IO.Class (MonadIO(..))
import Data.GraphQL
    ( MonadGraphQLQuery
    , GraphQLSettings(..)
    , defaultGraphQLSettings
    , get
    , runGraphQLQueryT
    , runQuery
    )
import qualified Data.Text as Text

import Example.GraphQL.API

app :: (MonadGraphQLQuery m, MonadIO m) => m ()
app = do
  song <- Text.pack <$> liftIO getLine

  result <- runQuery GetRecordingsQuery
    { _query = song
    , _first = Just 5
    }

  -- See the `aeson-schemas` package for more information on this syntax
  let songs = [get| result.search!.recordings!.nodes![]! |]
  liftIO $ print $ map [get| .title! |] songs

main :: IO ()
main = do
  let graphQLSettings = defaultGraphQLSettings
        { url = "https://graphbrainz.herokuapp.com/"
          -- ^ Most GraphQL APIs are at the path `/graphql`, but not this one
        }

  runGraphQLQueryT graphQLSettings app

Configuration

The codegen.yml file should have the following format. All paths are relative to the codegen.yml file.

Testing

This library also provides utilities to test functions using GraphQL queries by mocking the GraphQL endpoints. For example, you might test the app function from the Quickstart with the following:

{-# LANGUAGE QuasiQuotes #-}

import Data.Aeson.QQ (aesonQQ)
import Data.GraphQL.TestUtils (ResultMock(..), mocked, runMockQueryT)

import Example (app)
import Example.GraphQL.API

main :: IO ()
main = do
  let mockedGetRecordings = mocked ResultMock
        { query = GetRecordingsQuery
            { _query = "My Song"
            , _first = Just 5
            }
        , result =
            [aesonQQ|
              {
                "search": {
                  "recordings": {
                    "nodes": []
                  }
                }
              }
            |]
        }

  -- should not hit the server
  result <- runMockQueryT app [mockedGetRecordings]

  -- test `result`, which should be the result hardcoded above