dillonkearns / elm-graphql / Graphql.Http

Send requests to your GraphQL endpoint. See this live code demo or the examples/ folder for some end-to-end examples. The builder syntax is inspired by Luke Westby's elm-http-builder package.

Data Types


type Request decodesTo

An internal request as it's built up. Once it's built up, send the request with Graphql.Http.send.


type HttpError
    = BadUrl String
    | Timeout
    | NetworkError
    | BadStatus Http.Metadata String
    | BadPayload Json.Decode.Error

An Http Error. A Request can fail in a few ways:


type alias Error parsedData =
RawError parsedData HttpError

An alias for the default kind of Error. See the RawError for the full type.


type RawError parsedData httpError
    = GraphqlError (GraphqlError.PossiblyParsedData parsedData) (List GraphqlError)
    | HttpError httpError

Represents the two types of errors you can get, an Http error or a GraphQL error. See the Graphql.Http.GraphqlError module docs for more details.

Begin Request Pipeline

queryRequest : String -> Graphql.SelectionSet.SelectionSet decodesTo Graphql.Operation.RootQuery -> Request decodesTo

Initialize a basic request from a Query. You can add on options with withHeader, withTimeout, withCredentials, and send it with Graphql.Http.send.

mutationRequest : String -> Graphql.SelectionSet.SelectionSet decodesTo Graphql.Operation.RootMutation -> Request decodesTo

Initialize a basic request from a Mutation. You can add on options with withHeader, withTimeout, withCredentials, and send it with Graphql.Http.send.

queryRequestWithHttpGet : String -> QueryRequestMethod -> Graphql.SelectionSet.SelectionSet decodesTo Graphql.Operation.RootQuery -> Request decodesTo

Exactly like queryRequest, but with an option to use the HTTP GET request method. You will probably want to use GetIfShortEnough, which uses GET if the full URL ends up being under 2000 characters, or POST otherwise, since some browsers don't support URLs over a certain length. GetIfShortEnough will typically do what you need. If you must use GET no matter what when hitting your endpoint, you can use AlwaysGet.

queryRequest always uses POST since some GraphQL API's don't support GET requests (for example, the Github API assumes that you are doing an introspection query if you make a GET request). But for semantic reasons, GET requests are sometimes useful for sending GraphQL Query requests. That is, a GraphQL Query does not perform side-effects on the server like a Mutation does, so a GET indicates this and allows some servers to cache requests. See this github thread from the Apollo project for more details.


type QueryRequestMethod
    = AlwaysGet
    | GetIfShortEnough

Union type to pass in to queryRequestWithHttpGet. Only applies to queries. Mutations don't accept this configuration option and will always use POST.

Configure Request Options

withHeader : String -> String -> Request decodesTo -> Request decodesTo

Add a header.

makeRequest : Cmd Msg
makeRequest =
    query
        |> Graphql.Http.queryRequest "https://api.github.com/graphql"
        |> Graphql.Http.withHeader "authorization" "Bearer <my token>"
        |> Graphql.Http.send (RemoteData.fromResult >> GotResponse)

withTimeout : Basics.Float -> Request decodesTo -> Request decodesTo

Add a timeout.

withCredentials : Request decodesTo -> Request decodesTo

Set with credentials to true. See the XMLHttpRequest/withCredentials docs to understand exactly what happens.

Under the hood, this will use either Http.riskyRequest or Http.riskyTask.

withQueryParams : List ( String, String ) -> Request decodesTo -> Request decodesTo

Add query params. The values will be Uri encoded.

makeRequest : Cmd Msg
makeRequest =
    query
        |> Graphql.Http.queryRequest "https://api.github.com/graphql"
        |> Graphql.Http.withQueryParams [ ( "version", "1.2.3" ) ]
        |> Graphql.Http.send (RemoteData.fromResult >> GotResponse)

withOperationName : String -> Request decodesTo -> Request decodesTo

Set an operation name. This is a meaningful and explicit name for your operation, very helpful for debugging and server-side logging. See https://graphql.org/learn/queries/#operation-name

makeRequest : Cmd Msg
makeRequest =
    query
        |> Graphql.Http.queryRequest "https://api.github.com/graphql"
        |> Graphql.Http.withOperationName "HeroNameAndFriends"
        |> Graphql.Http.send (RemoteData.fromResult >> GotResponse)

Perform Request

send : (Result (Error decodesTo) decodesTo -> msg) -> Request decodesTo -> Platform.Cmd.Cmd msg

Send the Graphql.Request You can use it on its own, or with a library like RemoteData.

import Graphql.Http
import Graphql.OptionalArgument exposing (OptionalArgument(..))
import RemoteData exposing (RemoteData)

type Msg
    = GotResponse RemoteData (Graphql.Http.Error Response) Response

makeRequest : Cmd Msg
makeRequest =
    query
        |> Graphql.Http.queryRequest "https://elm-graphql.onrender.com/"
        |> Graphql.Http.withHeader "authorization" "Bearer abcdefgh12345678"
        -- If you're not using remote data, it's just
        -- |> Graphql.Http.send GotResponse
        -- With remote data, it's as below
        |> Graphql.Http.send (RemoteData.fromResult >> GotResponse)

If any errors are present, you will get a GraphqlError that includes the details. GraphQL allows for partial data to be returned in the case of errors so you can inspect the data returned in the GraphqlError if you would like to try to recover any data that made it through in the response.

sendWithTracker : String -> (Result (Error decodesTo) decodesTo -> msg) -> Request decodesTo -> Platform.Cmd.Cmd msg

Exactly like Graphql.Http.request except it allows you to use the String passed in as the tracker to track and cancel requests using the core Elm Http package (see the Http.request docs)

toTask : Request decodesTo -> Task (Error decodesTo) decodesTo

Convert a Request to a Task. See Graphql.Http.send for an example of how to build up a Request.

Map Errors

mapError : (a -> b) -> Error a -> Error b

Map the error data if it is ParsedData.

discardParsedErrorData : Result (Error decodesTo) decodesTo -> Result (Error a) decodesTo

Useful when you don't want to deal with the recovered data if there is ParsedData. Just a shorthand for mapError that will turn any ParsedData into ().

This is helpful if you want to simplify your types, or if you are combining multiple results together and you need the error types to line up (but you don't care about recovering parsed data when there are GraphQL errors in the response).

In the examples below, notice the error type is now (Graphql.Http.Error ()).

You can use this when you call Graphql.Http.send like so:

import Graphql.Http

type Msg
    = GotResponse (Result (Graphql.Http.Error ()) Response)

makeRequest =
    query
        |> Graphql.Http.queryRequest "http://elm-graphql.herokuapp.com"
        |> Graphql.Http.send
            (Graphql.Http.discardParsedErrorData
                >> RemoteData.fromResult
                >> GotResponse
            )

Or if you are using the RemoteData package:

import Graphql.Http
import RemoteData exposing (RemoteData)

type Msg
    = GotResponse (RemoteData (Graphql.Http.Error ()) Response)

makeRequest =
    query
        |> Graphql.Http.queryRequest "http://elm-graphql.herokuapp.com"
        |> Graphql.Http.send
            (Graphql.Http.discardParsedErrorData
                >> RemoteData.fromResult
                >> GotResponse
            )

withSimpleHttpError : Result (Error parsedData) decodesTo -> Result (RawError parsedData Http.Error) decodesTo

Useful when you want to combine together an Http response with a Graphql request response.

-- this is just the type that our query decodes to
type alias Response =
    { hello : String }

type Msg
    = GotResponse (RemoteData (Graphql.Http.RawError Response Http.Error) Response)

request =
    query
        |> Graphql.Http.queryRequest "https://some-graphql-api.com"
        |> Graphql.Http.send
            (Graphql.Http.withSimpleHttpError
                >> RemoteData.fromResult
                >> GotResponse
            )

combinedResponses =
    RemoteData.map2 Tuple.pair
        model.graphqlResponse
        (model.plainHttpResponse |> RemoteData.mapError Graphql.Http.HttpError)

Error Handling Strategies

There are 3 possible strategies to handle GraphQL errors.

  1. parseableErrorAsSuccess If there is a GraphQL error (and the data was complete enough for the decoder to run successfully), pretend that there was no error at all.
  2. Not Implemented If there is a GraphQL error pretend it was a network error. There is currently no implementation for this strategy, if you think this would come in handy, please open a Github issue to describe your use case!
  3. Default This gives you full control. You get an accurate and exact picture of what happened (got a GraphQL error & could parse the body, got a GraphQL error and couldn't parse the body, got an http error, or got everything was successful). And you can handle each case explicitly.

parseableErrorAsSuccess : Result (Error decodesTo) decodesTo -> Result (Error ()) decodesTo

WARNING: When using this function you lose information. Make sure this is the approach you want before using this.

Treat responses with GraphQL errors as successful responses if the data can be parsed. If you want to use the successfully decoded data without ignoring the GraphQL error you can do a pattern match on the Graphql.Http.Error type (it contains PossiblyParsedData which is either unparsed with a raw Json.Decode.Value or else it contains successfully decoded data).