dillonkearns/elm-graphql/1.3.0

NOTE: the latest version is 5.0.12

Graphql.SelectionSet

The auto-generated code from the @dillonkearns/elm-graphql CLI will provide selection functions for Objects, Interfaces, and Unions in your GraphQL schema. These functions build up a Graphql.SelectionSet which describes a set of fields to retrieve. The SelectionSet is built up in a pipeline similar to how Json.Decode.Pipeline builds up decoders.

For example, if you had a top-level query human(id: ID!) which returns an object of type Human, you could build the following GraphQL query document:

query {
  human(id: 1001) {
    name
    id
  }
}

In this example, the SelectionSet on human is:

{
  name
  id
}

You could build up the above SelectionSet with the following dillonkearns/elm-graphql code:

import Api.Object
import Api.Object.Human as Human
import Graphql.SelectionSet exposing (SelectionSet, with)


type alias Human =
    { name : String
    , id : String
    }


hero : SelectionSet Hero Api.Interface.Human
hero =
    Human.selection Human
        |> with Human.name
        |> with Human.id

Note that all of the modules under Api. in this case are generated by running the @dillonkearns/elm-graphql command line tool.

The query itself is also a SelectionSet so it is built up similarly. See this live code demo for an example.

Field a typeLock
-> SelectionSet (a -> b) typeLock
-> SelectionSet b typeLock

Used to pick out fields on an object.

import Api.Enum.Episode as Episode exposing (Episode)
import Api.Object
import Api.Scalar
import Graphql.SelectionSet exposing (SelectionSet, with)


type alias Hero =
    { name : String
    , id : Api.Scalar.Id
    , appearsIn : List Episode
    }


hero : SelectionSet Hero Api.Interface.Character
hero =
    Character.commonSelection Hero
        |> with Character.name
        |> with Character.id
        |> with Character.appearsIn
hardcoded : a -> SelectionSet (a -> b) typeLock -> SelectionSet b typeLock

Include a hardcoded value.

    import Api.Enum.Episode as Episode exposing (Episode)
    import Api.Object
    import Graphql.SelectionSet exposing (SelectionSet, with, hardcoded)


    type alias Hero =
        { name : String
        , movie : String
        }


    hero : SelectionSet Hero Api.Interface.Character
    hero =
        Character.commonSelection Hero
            |> with Character.name
            |> hardcoded "Star Wars"
empty : SelectionSet () typeLock

Useful for Mutations when you don't want any data back.

import Api.Mutation as Mutation
import Graphql.Operation exposing (RootMutation)
import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with)


sendChatMessage : String -> SelectionSet () RootMutation
sendChatMessage message =
    Mutation.selection identity
        |> with (Mutation.sendMessage { message = message } SelectionSet.empty)
map : (a -> b) -> SelectionSet a typeLock -> SelectionSet b typeLock

Apply a function to change the result of decoding the SelectionSet.

succeed : a -> SelectionSet a typeLock

Instead of hardcoding a field like hardcoded, SelectionSet.succeed hardcodes an entire SelectionSet. This can be useful if you want hardcoded data based on only the type when using a polymorphic type (Interface or Union).

type alias Character =
    { details : Maybe HumanOrDroid
    , name : String
    }


type HumanOrDroid
    = Human
    | Droid


hero : SelectionSet Character Swapi.Interface.Character
hero =
    Character.selection Character
        [ Character.onDroid (SelectionSet.succeed Droid)
        , Character.onHuman (SelectionSet.succeed Human)
        ]
        |> with Character.name
fieldSelection : Field response typeLock -> SelectionSet response typeLock

Create a SelectionSet from a single Field.

import Api.Object
import Api.Object.Human as Human
import Graphql.SelectionSet exposing (SelectionSet)


humanSelection : SelectionSet String Api.Object.Human
humanSelection =
    SelectionSet.fieldSelection Human.name

Combining

(decodesTo1 -> decodesTo2 -> decodesToCombined)
-> SelectionSet decodesTo1 typeLock
-> SelectionSet decodesTo2 typeLock
-> SelectionSet decodesToCombined typeLock

Combine two SelectionSets into one, using the given combine function to merge the two data sets together.

SelectionSet a typeLock
-> SelectionSet (a -> b) typeLock
-> SelectionSet b typeLock

Include a SelectionSet within a SelectionSet. This is the equivalent of including a GraphQL fragment in plain GraphQL queries. This is a handy tool for modularizing your GraphQL queries.

(You can try the below query for yourself by pasting the query into the Github query explorer).

Let's say we want to query Github's GraphQL API like this:

{
  repository(owner: "dillonkearns", name: "elm-graphql") {
    nameWithOwner
    ...timestamps
    stargazers(first: 0) { totalCount }
  }
}


fragment timestamps on Repository {
  createdAt
  updatedAt
}

We could do the equivalent of the timestamps fragment with the timestampsFragment we define below.

import Github.Object
import Github.Object.Repository as Repository
import Github.Object.StargazerConnection
import Graphql.Field as Field exposing (Field)
import Graphql.Operation exposing (RootQuery)
import Graphql.OptionalArgument exposing (OptionalArgument(..))
import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, fieldSelection, with, withFragment)
import Iso8601
import Time exposing (Posix)


type alias Repo =
    { nameWithOwner : String
    , timestamps : Timestamps
    , stargazersCount : Int
    }


type alias Timestamps =
    { createdAt : Posix
    , updatedAt : Posix
    }


repositorySelection : SelectionSet Repo Github.Object.Repository
repositorySelection =
    Repository.selection Repo
        |> with Repository.nameWithOwner
        |> withFragment timestampsFragment
        |> with stargazersCount


timestampsFragment : SelectionSet Timestamps Github.Object.Repository
timestampsFragment =
    Repository.selection Timestamps
        |> with (Repository.createdAt |> mapToDateTime)
        |> with (Repository.updatedAt |> mapToDateTime)


mapToDateTime : Field Github.Scalar.DateTime typeLock -> Field Posix typeLock
mapToDateTime =
    Field.mapOrFail
        (\(Github.Scalar.DateTime value) ->
            Iso8601.toTime value
                |> Result.mapError (\_ -> "Failed to parse " ++ value ++ " as Iso8601 DateTime.")
        )


stargazersCount : Field Int Github.Object.Repository
stargazersCount =
    Repository.stargazers
        (\optionals -> { optionals | first = Present 0 })
        (fieldSelection Github.Object.StargazerConnection.totalCount)

Notice that we are using two different techniques for abstraction here. We use |> withFragment timestampsFragment to include a fragment and we use |> with stargazersCount. What's the difference? For the timestampsFragment, we want to pull in some fields at the same level of the SelectionSet we are building up. But for the stargazersCount, we are simply extracting a Field which builds up a nested SelectionSet that grabs the totalCount within the stargazers Field.

Types

These types are built for you by the code generated by the @dillonkearns/elm-graphql command line tool.

type SelectionSet decodesTo typeLock
= SelectionSet (List RawField) (Decoder decodesTo)

SelectionSet type

type FragmentSelectionSet decodesTo typeLock
= FragmentSelectionSet String (List RawField) (Decoder decodesTo)

FragmentSelectionSet type