GraphQL queries as Clojure data structures, Hiccup style.
hicgql is in Clojars. Just add it.
[io.github.timrichardt/hicgql "0.3.1"]io.github.timrichardt/hicgql {:mvn/version "0.3.1"}(:require
[hicgql.core :refer [graphql]])GraphQL documents are described with nested vectors, where the first element of each vector is a keyword describing the type of the document element.
[:<ELEMENT-TYPE> :<PROPS?> :<CHILD? OR CHILDREN?>]hicgql.core/graphql accepts an arbitrary number of operations and renders them concatenated with a\n inbetween.
Operations are described with :*/ namespaced keywords, e.g. :*/OperationName. Valid types are :query, :mutation, :subscription, and can be set via :*/type. Variables are supplied as :$var "Type" pairs.
(graphql
[:*/Op {:*/type :query
:$var "Type"
:$var2 ["Type2" "default-value"]} :id])query Op($var: Type, $var2: Type2 = "default-value") {
id
}Fields with subfields are prefixed by :+/ namespace. Data fields without subfields do not have a namespace.
[:+/fieldWithSubfields
:subfield1
:subfield2
:.../fragment
[:+/anotherSubfieldedField
:andSoOn]]{
fieldWithSubfields {
subfield1
subfield2
...fragment
anotherSubfieldedField {
andSoOn
}
}
}Arguments to fields can be set with a map which is the second element of the selection vector. Argument names are the keys, the values can be Clojure data structures that can be meaningfully translated with
js/JSON.stringify or cheshire.core/generate-string.
[:+/_
[:fieldWithArgs {:stringArg "string"
:numberArg 2
:objArg {:size [27.32 "cm"]}}]]{
fieldWithArgs(stringArg: "string", numberArg: 2, objArg: {size: [27.32 "cm"]})
}To define a GraphQL document, that does not start with an operation, there is :+/_: [:+/_ field] → { field }.
Directives can be set with the :! key in the property map. :!'s value has to be a list of directives. A directive is either a key :directive → @directive, or can be supplied with arguments, like a field.
[:+/_
[:fieldWithDirs {:! [[:dir2 {:arg1 :$var
:arg2 'VAL}]
:dir1]}]]{
fieldWithDirs @dir2(arg1: $var, arg2: VAL) @dir1
}Directives are applied from bottom to top.
Aliases can be set with the :>/ namespace. The name of the keyword is the name of the aliased field.
[:+/_
[:>/alias [:someField {:arg "val"}]]]{
alias: someField(arg: "val")
}Inline fragments are defined with :?/ prefixed keywords. The name of the keyword has to be the type the fragment is of.
[:+/_
[:?/TypeA :id]]{
... on TypeA {
id
}
}Fragments are defined as operations, but with :§/ namespaced keywords. The fragment type has to be set via the :on property.
[:§/Fragment {:on :Type}
:field]fragment Fragment on Type {
field
}It is possible, to use for example for, to generate a list of fields.
[:+/_
(for [m ["M" "N"]]
(for [a ["A" "B" "C"]
x ["X" "Y" "Z"]]
(keyword (str a x m))))]{AXM,AYM,AZM,BXM,BYM,BZM,CXM,CYM,CZM,AXN,AYN,AZN,BXN,
BYN,BZN,CXN,CYN,CZN,AXP,AYP,AZP,BXP,BYP,BZP,CXP,CYP,CZP}re-graph adds the operation type itself, so you don't need to add the :*/type keyword to the property map.
(re-graph/query
:query-id
(graphql
[:*/MyQuery {:$var "String"}
[:+/selection {:arg :$var}
:subfield]])
{:var "value"}
callback)- GraphQL Spec October 2021, http://spec.graphql.org/October2021/
- Hiccup by James Reeves, https://github.com/weavejester/hiccup
- re-graph by Oliver Hine, https://github.com/oliyh/re-graph
Eclipse Public License https://www.eclipse.org/legal/epl-v10.html