An alternative way to build Graphql interfarces powered by Pathom 3 and Lacinia.
Graffito takes a Lacinia Schema with a few extra attributes that allow Lacinia to resolve Graphql queries using Pathom 3 resolvers. This means you write Pathom3 resolvers/mutations and with minimal configuration you can also offer a Graphql graphql
To expose a Pathom 3 graph as Graphql first you need to have Pathom3 graph. Nothing in particular has to be done when you write the pathom 3 mutations and resolvers. Once the pathom 3 graph is available you write a lacinia schema that describes the objects and queries that we are going to expose in the graphql app.
By convention Graffito matches Graphql types & attributes to namespaced attributes resolved by pathom. If the convention doesn't match the existing attributes, you can specifiy the name of a give type attribute.
The default convetion is to convert any attribute of a graphql type to: type.attribte-name
. Example: the Graphql attribute min_players
from the type BoardGame
is by default resolved by a pathom as :board-game.min-players
. In this case, no configuration was necessary inside the lacinia schema.
To match an attribute to a different pathom attribute you use :pathom/attribute
to specify the name of the attribute to match the Grapqhl attr.
Example:
:GameRating
{:description "A member's rating of a particular game."
:fields
{:game {:type (non-null :BoardGame)
:description "The Game rated by the member."}
:rating {:type (non-null Int)
:description "The rating as 1 to 5 stars."
:pathom/attribute :rating/value}}} ;; Here we are saying that we want to match GameRating.rating to the pathom attr :rating/value
If not specified each query in GraphQl will be resolved by the default Lacinia resolver. This resolver basically calls pathom and converts the attributes to the values expected by the Lacinia resolver. As in any other Lacinia app, a custom resolver can be specified using :resolve :your/resolver
.
For mutations you need to specify the corresponding Pathom3 mutation. Example:
:mutations
{:rate_game
{:type :BoardGame
:description "Establishes a rating of a board game, by a Member.
On success (the game and member both exist), selects the BoardGame.
Otherwise, selects nil and an error."
:args
{:game_id {:type (non-null ID)
:pathom/attribute :board-game/id}
:member_id {:type (non-null ID)
:pathom/attribute :member/id}
:rating {:type (non-null Int)
:description "Game rating as a number between 1 and 5."}}
:resolve [:pathom/mutation graffito.board-game-geek.resolver/rate!]}} ;; Specify the corresponding pathom mutation
Graphql Subscriptions are not yet supported :(
(ns graffito.board-game-geek.core
(:require [com.walmartlabs.lacinia :as lacinia)
[graffito.core :as graffito]
(def pathom-index ....)
(def schema
(graffito/compile (graffito/load-schema! "cgg-schema.edn")
pathom-index))
(lacinia/execute schema "{ game_by_id (id: \"1236\") { id name }}" nil pathom-index)
For more complete examples check This test
Pathom 3 liberates you from the Type hell that Graphql forces you into, so why do we wnat to use Graphql? Reach. Graphql lets you reach to the Javascript community. A lot of tools have been developed around graphql and convincing a javascript developr to use EQL is as hard as convincing a Clojurescript developer to use Strings to write queries. Graffito opens your graph to both audiences.
We have been using Graffito internally in a medium size Pathom 3 app. The graphql queries we have tested in top of Graffito are relatively simple. We haven't yet tested more advanced features like Fragment, aliases, variables. ETC.
Graffito has been heavily inspired by the ides from Graffiti and this discussion.
Copyright © 2021 Juan.maya
EPLv1.0 is just the default for projects generated by clj-new
: you are not
required to open source this project, nor are you required to use EPLv1.0!
Feel free to remove or change the LICENSE
file and remove or update this
section of the README.md
file!
Distributed under the Eclipse Public License version 1.0.