graphql-client
A client for Haskell programs to query a GraphQL API
https://github.com/brandonchinn178/graphql-client#readme
LTS Haskell 23.1: | 1.2.4 |
Stackage Nightly 2024-12-09: | 1.2.4 |
Latest on Hackage: | 1.2.4 |
graphql-client-1.2.4@sha256:ce3a3b92b8c99be1851b11ca809f9dc2a800363e01b5f251054e6047afe8b45e,3109
Module documentation for 1.2.4
graphql-client
A client for Haskell applications to query GraphQL APIs. This package provides two resources:
-
A
graphql-codegen
executable that can generate Haskell definitions from input.graphql
files -
The
graphql-client
Haskell library providing arunQuery
function that takes in a query type generated bygraphql-codegen
Quickstart
Pre-requisites: Have Node.js installed.
-
Add
graphql-client
as a dependency to yourpackage.yaml
or Cabal file -
stack build --only-dependencies
-
Write the
.graphql
queries you wish to use. -
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.
-
Write the module specified in
scalarsModule
(e.g.src/Example/GraphQL/Scalars.hs
). See the “Configuration” section for more details. -
stack exec graphql-codegen
-
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.fly.dev"
-- ^ 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.
-
schema
: Where to get the schema of the entire GraphQL API. Can be one of the following:- A URL pointing to the GraphQL API
- The path to a local JSON file containing the result of a GraphQL Introspection query
- The path to a local
.graphql
file containing the schema in GraphQL format
-
documents
: A string or list of strings containing glob expressions to load the.graphql
files containing the GraphQL queries you wish to use. -
hsSourceDir
: The directory (relative tocodegen.yml
) to generate the modules. Should be one of the directories in thehs-source-dirs
field in your Cabal file. A moduleX.Y.Z
would be generated at<hsSourceDir>/X/Y/Z.hs
. Defaults tosrc/
. -
apiModule
: The module that will be generated with the Haskell definitions corresponding to the.graphql
input files specified bydocuments
. -
enumsModule
: The module where GraphQL enums will be generated. Only the enums you actually use in your queries will be generated, with a module generated per enum. For example, if your queries use aColor
enum andenumsModule
is set toExample.GraphQL.Enums
,graphql-codegen
will generate theExample.GraphQL.Enums.Color
module. -
scalarsModule
: The module where custom GraphQL scalars should be exported. You may define the scalars in other modules, but you must re-export them in this module. If you’re not using any custom scalars in your queries, this module can be empty (but must still exist). All GraphQL scalars must haveFromJSON
andToJSON
instances.
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
Changes
v1.2.4
- Fix ambiguous field name error with same argument name in multiple queries (#86)
v1.2.3
- Add support for GHC 9.8
- Drop support for GHC < 9.4
v1.2.2
- Add support for GHC 9.6
- Fix escaping issues in generated code (e.g. literal strings in graphql queries) (#80)
v1.2.1
- Add support for GHC 9.4
v1.2.0
Breaking changes:
- Remove support for GHC < 8.10
New features:
- Added
runQuerySafeIO
and exposeGraphQLManager
for applications that want to manually implementMonadGraphQLQuery
v1.1.1
Bug fixes:
- Generate enums that only appear in query arguments (#59)
v1.1.0
Breaking changes:
- Require
aeson-schemas-1.3.0
TypeApplications
is no longer needed forget
quasiquoters- See
aeson-schemas
CHANGELOG for more details
- Scalars now also need a
ToJSON
instance
Miscellaneous changes:
- Improved test-utils UX:
- Export
AnyResultMock
- Add
Show
instance forAnyResultMock
- Add
MonadTrans
instance forMockQueryT
- Export
v1.0.0
Initial release:
- Implement
graphql-client
Haskell library with agraphql-codegen
executable that can generate Haskell definitions for.graphql
files.