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.idNote 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.
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"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)Apply a function to change the result of decoding the SelectionSet.
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.nameCreate 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.nameCombine two SelectionSets into one, using the given combine function to
merge the two data sets together.
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.
These types are built for you by the code generated by the @dillonkearns/elm-graphql command line tool.
SelectionSet type
FragmentSelectionSet type
Used to pick out fields on an object.