Repository: wilkerlucio/pathom
Pull request #75 (2019-02-16 14:37:48)

Graphql connect raw


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 074fb04c..e3224515 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Change Log
 
+## [2.2.12]
+- Move core.async and test.check to main dependencies
+- Add missing specs for: `::pc/resolve`, `::pc/resolver`, `::pc/mutate` and `::pc/mutation`
+- Add `com.wsscode.pathom.connect.graphql2` ns, using raw graphql translations
+
 ## [2.2.11]
 - Fix error propagation for uncached resolvers
 - Fix timeout issue case when chained dependencies cause an error in the middle
diff --git a/docs/index.adoc b/docs/index.adoc
index 8ea4cae8..26e3ecbf 100644
--- a/docs/index.adoc
+++ b/docs/index.adoc
@@ -2571,11 +2571,21 @@ This chapter assumes you're familiar with Pathom's <<AsyncParsing, async support
 
 The namespaces concerned are:
 
-```
-    [com.wsscode.pathom.graphql :as pg]
-    [com.wsscode.pathom.connect.graphql :as pcg]
-    [com.wsscode.pathom.fulcro.network :as pfn]
-```
+[source,clojure]
+----
+[com.wsscode.pathom.graphql :as pg]
+[com.wsscode.pathom.connect.graphql2 :as pcg]
+[com.wsscode.pathom.fulcro.network :as pfn]
+----
+
+NOTE: Before Pathom 2.2.12 the default functions to work with GraphQL used to convert
+the standard Clojure hyphenated to GraphQL camel case format, but after some user reports
+we realized that wasn't a good idea because some names could never be accessed when entry
+points started with capital letters. To avoid those problems, since Pathom 2.2.12 we
+recommend new implementations that don't transform the names in any way by default, but
+at same time provides custom name munging if the user wants to use it. None of the previous
+code was changed so library clients will not break with this change, we are just using
+new namespaces that use the new simpler way.
 
 == Simple GraphQL
 
@@ -2586,19 +2596,18 @@ support to a Fulcro client like so:
 (fulcro/new-fulcro-client
     :networking
     {:remote
-     (pfn/graphql-network
+     (pfn/graphql-network2
        {::pfn/url (str "https://api.github.com/graphql?access_token=" token)})})
 ```
 
 The queries from components have the following rules:
 
 . You can use any namespace on the query keywords.
-. The `name` portion of a keyword can be hyphenated instead of camel-case
-.. Hyphenated names will automatically be converted to the standard GraphQL camel case.
+. The `name` portion of a keyword will be used to send to GraphQL
 
 Mutations on a Simple GraphQL remote have the following rules:
 
-. Mutations can have any namespace.  The GraphQL conversion will elide the namespace.
+. Mutations can have any namespace. The GraphQL conversion will elide the namespace.
 
 === Simple GraphQL Example
 
@@ -2642,29 +2651,29 @@ can write a simple Fulcro tree of components for that query:
 
 (defsc TodoSimpleDemo [this props]
   {:ident         (fn [] [::root "singleton"])
-   :query         [{:all-todo-items (fp/get-query TodoItem)}]}
+   :query         [{:allTodoItems (fp/get-query TodoItem)}]}
   ...)
 ```
 
 Notice that on `TodoItem` we namespaced the keys.  This is fine, as the integration code will strip these from the
-query.  If `TodoSimpleDemo` were your root component, the query for it is *already* compatible with our defined API
+query. If `TodoSimpleDemo` were your root component, the query for it is *already* compatible with our defined API
 when using our GraphQL network:
 
 ```
 (fulcro/new-fulcro-client
   :started-callback
   (fn [app]
-    (df/load app :all-todo-items todo/TodoItem {:target [::root "singleton" :all-todo-items]}))
+    (df/load app :allTodoItems todo/TodoItem {:target [::root "singleton" :allTodoItems]}))
 
   :networking
-  {:remote (pfn/graphql-network "https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk")})
+  {:remote (pfn/graphql-network2 "https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk")})
 ```
 
 Mutations are similarly easy. The network component translates them as discussed earlier, so doing something like
 adding a new todo item likes like this:
 
 ```
-(fm/defmutation create-todo-item [todo]
+(fm/defmutation createTodoItem [todo]
   (action [env] ...local optimistic stuff...)
   (remote [{:keys [ast]}]
     ;; Don't send the UI-specific params to the server...just the id and title
@@ -2676,7 +2685,7 @@ going pretty quickly.
 
 [source,clojure]
 ----
-include::../src-docs/com/wsscode/pathom/book/graphql/fulcro_network/graphql_todo.cljs[]
+include::../workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs
 ----
 
 == GraphQL and Connect
@@ -2685,22 +2694,21 @@ The more powerful way to use GraphQL from Pathom is to use it with Connect.  Thi
 you saw in the simple version, but also gives you a lot more power and extensibility.
 
 The integration has a bit of boilerplate, but it's all relatively simple.  Please make sure you already
-understand <<Connect,Connect>> before reading this.
+understand <<Connect>> before reading this.
 
 === Keywords and GraphQL – Prefixes
 
 In order to properly generate indexes Connect needs to know how you will prefix them for a given GraphQL endpoint.
 From there, the keyword also gives an indication of the "type" and attribute name.
 
-Say we are interfacting with GitHub: we might choose the prefix `github`.  Then our keywords would need to be
-things like `:github.user/name`.  The name portion of the keyword can be hyphenated, and Pathom will turn that
-into a camel-case name, since that is the standard for GraphQL.  Name munging is customizable via Pathom settings.
+Say we are interfacting with GitHub: we might choose the prefix `github`. Then our keywords would need to be
+things like `:github.User/name`.
 
 You will have to formally declare the prefix you've decided on in order to Connect to work.
 
 === GraphQL Entry Points and Connect Ident Maps
 
-In GraphQL the schema designer indicates what entry points are possible.  In GitHub's public API you can, for example,
+In GraphQL the schema designer indicates what entry points are possible. In GitHub's public API you can, for example,
 access a `User` if you know their `login`.  You can access a `Repository` if you know *both* the `owner`
 and the repository `name`.
 
@@ -2730,7 +2738,7 @@ Our EDN queries use idents to stand for these kind of entry points.  So, we'd li
 like this:
 
 ```
-[{[:github.user/login "wilkerlucio"] [:github.user/created-at]}]
+[{[:github.User/login "wilkerlucio"] [:github.User/createdAt]}]
 ```
 
 into the GraphQL query above.  This is the purpose of the "Ident Map".  It is a map whose top-level keys are GraphQL
@@ -2746,9 +2754,9 @@ EDN keywords:
 So, an ident map for the above two GraphQL entry points is:
 
 ```
-{"user"       {"login" :github.user/login}
- "repository" {"owner" :github.user/login
-               "name"  :github.repository/name}}
+{"user"       {"login" :github.User/login}
+ "repository" {"owner" :github.User/login
+               "name"  :github.Repository/name}}
 ```
 
 Installing such an ident map (covered shortly) will enable this feature.
@@ -2779,9 +2787,9 @@ We need to define the configuration for the GraphQL connection:
 (def github-gql
   {::pcg/url       (str "https://api.github.com/graphql?access_token=" (ls/get :github-token))
    ::pcg/prefix    "github"
-   ::pcg/ident-map {"user"       {"login" ["User" "login"]}
-                    "repository" {"owner" ["User" "login"]
-                                  "name"  ["Repository" "name"]}}
+   ::pcg/ident-map {"user"       {"login" :github.User/login}
+                    "repository" {"owner" :github.User/login
+                                  "name"  :github.Repository/name}}
    ::p.http/driver p.http.fetch/request-async})
 ```
 
@@ -2852,21 +2860,21 @@ into our context!
 
 ```
 (pc/defresolver repositories [_ _]
-  {::pc/output [{:demo-repos [:github.user/login :github.repository/name]}]}
+  {::pc/output [{:demo-repos [:github.User/login :github.Repository/name]}]}
   {:demo-repos
-   [{:github.user/login "wilkerlucio" :github.repository/name "pathom"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-inspect"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-css"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-spec"}
-    {:github.user/login "thheller" :github.repository/name "shadow-cljs"}]})
+   [{:github.User/login "wilkerlucio" :github.Repository/name "pathom"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-inspect"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-css"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-spec"}
+    {:github.User/login "thheller" :github.Repository/name "shadow-cljs"}]})
 ```
 
 Remember, once Connect has enough info in a context, it can fill in the remaining details. Our Ident Map indicates
 that if we have "user login" and "repository name", then we can get a repository.  Thus, a resolver that outputs
 values for the keywords associated with those requirements is sufficient!
 
-Remember to add this mutation definition before the parser, then we have to add this resolver
+Remember to add this resolver definition before the parser, then we have to add this resolver
 to our connect system, do that by updating the call to the `connect-plugin`, here is the updated
 parser:
 
@@ -2887,7 +2895,7 @@ parser:
                   p/trace-plugin]}))
 ```
 
-Now we can run a query on `:demo-repos` like `[{:demo-repos [:github.repository/created-at]}]`, and walk the graph
+Now we can run a query on `:demo-repos` like `[{:demo-repos [:github.Repository/createdAt]}]`, and walk the graph
 from there to anywhere allowed!
 
 === Queries
@@ -2897,14 +2905,14 @@ GraphQL schema for your API.  The EDN queries in Fulcro might look like this:
 
 ```
 (fp/defsc Repository
-  [this {:github.repository/keys [id name-with-owner viewer-has-starred]}]
-  {:ident         [:github.repository/id :github.repository/id]
-   :query         [:github.repository/id :github.repository/name-with-owner :github.repository/viewer-has-starred]}
+  [this {:github.Repository/keys [id nameWithOwner viewerHasStarred]}]
+  {:ident [:github.Repository/id :github.Repository/id]
+   :query [:github.Repository/id :github.Repository/nameWithOwner :github.Repository/viewerHasStarred]}
   ...)
 
 (fp/defsc GraphqlDemo
   [this {:keys [demo-repos]}]
-  {:query         [{:demo-repos (fp/get-query Repository)}]}
+  {:query [{:demo-repos (fp/get-query Repository)}]}
   (dom/div
     (mapv repository demo-repos)))
 ```
@@ -2918,12 +2926,12 @@ If you're using Fulcro, then the normal method of defining mutations will work i
 simply prefix the mutation name with your GraphQL `prefix` and it'll work:
 
 ```
-(fm/defmutation github/add-star [_]
+(fm/defmutation github/addStar [_]
   (action [{:keys [state ref]}] ...)
   (remote [_] true))
 ```
 
-IMPORTANT: This is *not* the `defmutation` we showed earlier in the setup.  This is Fulcro's `defmutation`.
+IMPORTANT: This is *not* the `defmutation` we showed earlier in the setup. This is Fulcro's `defmutation`.
 
 You can, of course, modify the parameters, do mutation joins, etc.
 
diff --git a/docs/index.html b/docs/index.html
index 73d6aef2..9ea47bd5 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -4354,11 +4354,30 @@ support into a Fulcro application as a remote!</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">    [com.wsscode.pathom.graphql <span class="symbol">:as</span> pg]
-    [com.wsscode.pathom.connect.graphql <span class="symbol">:as</span> pcg]
-    [com.wsscode.pathom.fulcro.network <span class="symbol">:as</span> pfn]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="clojure">[com.wsscode.pathom.graphql <span class="symbol">:as</span> pg]
+[com.wsscode.pathom.connect.graphql2 <span class="symbol">:as</span> pcg]
+[com.wsscode.pathom.fulcro.network <span class="symbol">:as</span> pfn]</code></pre>
 </div>
 </div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<div class="title">Note</div>
+</td>
+<td class="content">
+Before Pathom 2.2.12 the default functions to work with GraphQL used to convert
+the standard Clojure hyphenated to GraphQL camel case format, but after some user reports
+we realized that wasn&#8217;t a good idea because some names could never be accessed when entry
+points started with capital letters. To avoid those problems, since Pathom 2.2.12 we
+recommend new implementations that don&#8217;t transform the names in any way by default, but
+at same time provides custom name munging if the user wants to use it. None of the previous
+code was changed so library clients will not break with this change, we are just using
+new namespaces that use the new simpler way.
+</td>
+</tr>
+</table>
+</div>
 <div class="sect2">
 <h3 id="_simple_graphql"><a class="anchor" href="#_simple_graphql"></a><a class="link" href="#_simple_graphql">6.1. Simple GraphQL</a></h3>
 <div class="paragraph">
@@ -4370,7 +4389,7 @@ support to a Fulcro client like so:</p>
 <pre class="CodeRay highlight"><code data-lang="clojure">(fulcro/new-fulcro-client
     <span class="symbol">:networking</span>
     {<span class="symbol">:remote</span>
-     (pfn/graphql-network
+     (pfn/graphql-network2
        {<span class="symbol">::pfn/url</span> (<span class="keyword">str</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">https://api.github.com/graphql?access_token=</span><span class="delimiter">&quot;</span></span> token)})})</code></pre>
 </div>
 </div>
@@ -4383,14 +4402,7 @@ support to a Fulcro client like so:</p>
 <p>You can use any namespace on the query keywords.</p>
 </li>
 <li>
-<p>The <code>name</code> portion of a keyword can be hyphenated instead of camel-case</p>
-<div class="olist loweralpha">
-<ol class="loweralpha" type="a">
-<li>
-<p>Hyphenated names will automatically be converted to the standard GraphQL camel case.</p>
-</li>
-</ol>
-</div>
+<p>The <code>name</code> portion of a keyword will be used to send to GraphQL</p>
 </li>
 </ol>
 </div>
@@ -4400,7 +4412,7 @@ support to a Fulcro client like so:</p>
 <div class="olist arabic">
 <ol class="arabic">
 <li>
-<p>Mutations can have any namespace.  The GraphQL conversion will elide the namespace.</p>
+<p>Mutations can have any namespace. The GraphQL conversion will elide the namespace.</p>
 </li>
 </ol>
 </div>
@@ -4453,13 +4465,13 @@ can write a simple Fulcro tree of components for that query:</p>
 
 (defsc TodoSimpleDemo [this props]
   {<span class="symbol">:ident</span>         (<span class="keyword">fn</span> [] [<span class="symbol">::root</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">singleton</span><span class="delimiter">&quot;</span></span>])
-   <span class="symbol">:query</span>         [{<span class="symbol">:all-todo-items</span> (fp/get-query TodoItem)}]}
+   <span class="symbol">:query</span>         [{<span class="symbol">:allTodoItems</span> (fp/get-query TodoItem)}]}
   <span class="keyword">..</span><span class="keyword">.</span>)</code></pre>
 </div>
 </div>
 <div class="paragraph">
 <p>Notice that on <code>TodoItem</code> we namespaced the keys.  This is fine, as the integration code will strip these from the
-query.  If <code>TodoSimpleDemo</code> were your root component, the query for it is <strong>already</strong> compatible with our defined API
+query. If <code>TodoSimpleDemo</code> were your root component, the query for it is <strong>already</strong> compatible with our defined API
 when using our GraphQL network:</p>
 </div>
 <div class="listingblock">
@@ -4467,10 +4479,10 @@ when using our GraphQL network:</p>
 <pre class="CodeRay highlight"><code data-lang="clojure">(fulcro/new-fulcro-client
   <span class="symbol">:started-callback</span>
   (<span class="keyword">fn</span> [app]
-    (df/load app <span class="symbol">:all-todo-items</span> todo/TodoItem {<span class="symbol">:target</span> [<span class="symbol">::root</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">singleton</span><span class="delimiter">&quot;</span></span> <span class="symbol">:all-todo-items</span>]}))
+    (df/load app <span class="symbol">:allTodoItems</span> todo/TodoItem {<span class="symbol">:target</span> [<span class="symbol">::root</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">singleton</span><span class="delimiter">&quot;</span></span> <span class="symbol">:allTodoItems</span>]}))
 
   <span class="symbol">:networking</span>
-  {<span class="symbol">:remote</span> (pfn/graphql-network <span class="string"><span class="delimiter">&quot;</span><span class="content">https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk</span><span class="delimiter">&quot;</span></span>)})</code></pre>
+  {<span class="symbol">:remote</span> (pfn/graphql-network2 <span class="string"><span class="delimiter">&quot;</span><span class="content">https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk</span><span class="delimiter">&quot;</span></span>)})</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -4479,7 +4491,7 @@ adding a new todo item likes like this:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">(fm/defmutation create-todo-item [todo]
+<pre class="CodeRay highlight"><code data-lang="clojure">(fm/defmutation createTodoItem [todo]
   (action [env] <span class="keyword">..</span><span class="keyword">.</span>local optimistic stuff.<span class="keyword">..</span>)
   (remote [{<span class="symbol">:keys</span> [ast]}]
     <span class="comment">;; Don't send the UI-specific params to the server...just the id and title</span>
@@ -4492,7 +4504,7 @@ going pretty quickly.</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">Unresolved directive in index.adoc <span class="keyword">-</span> include<span class="symbol">::..</span>/src-docs/com/wsscode/pathom/book/graphql/fulcro_network/graphql_todo.cljs[]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="clojure">include<span class="symbol">::..</span>/workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs</code></pre>
 </div>
 </div>
 </div>
@@ -4505,7 +4517,7 @@ you saw in the simple version, but also gives you a lot more power and extensibi
 </div>
 <div class="paragraph">
 <p>The integration has a bit of boilerplate, but it&#8217;s all relatively simple.  Please make sure you already
-understand <a href="#Connect">Connect</a> before reading this.</p>
+understand <a href="#Connect">Pathom Connect</a> before reading this.</p>
 </div>
 <div class="sect3">
 <h4 id="_keywords_and_graphql_prefixes"><a class="anchor" href="#_keywords_and_graphql_prefixes"></a><a class="link" href="#_keywords_and_graphql_prefixes">6.2.1. Keywords and GraphQL – Prefixes</a></h4>
@@ -4514,9 +4526,8 @@ understand <a href="#Connect">Connect</a> before reading this.</p>
 From there, the keyword also gives an indication of the "type" and attribute name.</p>
 </div>
 <div class="paragraph">
-<p>Say we are interfacting with GitHub: we might choose the prefix <code>github</code>.  Then our keywords would need to be
-things like <code>:github.user/name</code>.  The name portion of the keyword can be hyphenated, and Pathom will turn that
-into a camel-case name, since that is the standard for GraphQL.  Name munging is customizable via Pathom settings.</p>
+<p>Say we are interfacting with GitHub: we might choose the prefix <code>github</code>. Then our keywords would need to be
+things like <code>:github.User/name</code>.</p>
 </div>
 <div class="paragraph">
 <p>You will have to formally declare the prefix you&#8217;ve decided on in order to Connect to work.</p>
@@ -4525,7 +4536,7 @@ into a camel-case name, since that is the standard for GraphQL.  Name munging is
 <div class="sect3">
 <h4 id="_graphql_entry_points_and_connect_ident_maps"><a class="anchor" href="#_graphql_entry_points_and_connect_ident_maps"></a><a class="link" href="#_graphql_entry_points_and_connect_ident_maps">6.2.2. GraphQL Entry Points and Connect Ident Maps</a></h4>
 <div class="paragraph">
-<p>In GraphQL the schema designer indicates what entry points are possible.  In GitHub&#8217;s public API you can, for example,
+<p>In GraphQL the schema designer indicates what entry points are possible. In GitHub&#8217;s public API you can, for example,
 access a <code>User</code> if you know their <code>login</code>.  You can access a <code>Repository</code> if you know <strong>both</strong> the <code>owner</code>
 and the repository <code>name</code>.</p>
 </div>
@@ -4562,7 +4573,7 @@ like this:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">[{[<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span>] [<span class="symbol">:github.user/created-at</span>]}]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="clojure">[{[<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span>] [<span class="symbol">:github.User/createdAt</span>]}]</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -4582,9 +4593,9 @@ EDN keywords:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">{<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.user/login</span>}
- <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.user/login</span>
-               <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  <span class="symbol">:github.repository/name</span>}}</code></pre>
+<pre class="CodeRay highlight"><code data-lang="clojure">{<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>}
+ <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>
+               <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  <span class="symbol">:github.Repository/name</span>}}</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -4633,9 +4644,9 @@ loaded asynchronosly later and we need the index reference to add the GraphQL co
 <pre class="CodeRay highlight"><code data-lang="clojure">(<span class="keyword">def</span> <span class="function">github-gql</span>
   {<span class="symbol">::pcg/url</span>       (<span class="keyword">str</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">https://api.github.com/graphql?access_token=</span><span class="delimiter">&quot;</span></span> (ls/get <span class="symbol">:github-token</span>))
    <span class="symbol">::pcg/prefix</span>    <span class="string"><span class="delimiter">&quot;</span><span class="content">github</span><span class="delimiter">&quot;</span></span>
-   <span class="symbol">::pcg/ident-map</span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> [<span class="string"><span class="delimiter">&quot;</span><span class="content">User</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span>]}
-                    <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> [<span class="string"><span class="delimiter">&quot;</span><span class="content">User</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span>]
-                                  <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  [<span class="string"><span class="delimiter">&quot;</span><span class="content">Repository</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>]}}
+   <span class="symbol">::pcg/ident-map</span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>}
+                    <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>
+                                  <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  <span class="symbol">:github.Repository/name</span>}}
    <span class="symbol">::p.http/driver</span> p.http.fetch/request-async})</code></pre>
 </div>
 </div>
@@ -4742,14 +4753,14 @@ into our context!</p>
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="clojure">(pc/defresolver repositories [_ _]
-  {<span class="symbol">::pc/output</span> [{<span class="symbol">:demo-repos</span> [<span class="symbol">:github.user/login</span> <span class="symbol">:github.repository/name</span>]}]}
+  {<span class="symbol">::pc/output</span> [{<span class="symbol">:demo-repos</span> [<span class="symbol">:github.User/login</span> <span class="symbol">:github.Repository/name</span>]}]}
   {<span class="symbol">:demo-repos</span>
-   [{<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">pathom</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-inspect</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-css</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-spec</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">thheller</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">shadow-cljs</span><span class="delimiter">&quot;</span></span>}]})</code></pre>
+   [{<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">pathom</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-inspect</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-css</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-spec</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">thheller</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">shadow-cljs</span><span class="delimiter">&quot;</span></span>}]})</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -4758,7 +4769,7 @@ that if we have "user login" and "repository name", then we can get a repository
 values for the keywords associated with those requirements is sufficient!</p>
 </div>
 <div class="paragraph">
-<p>Remember to add this mutation definition before the parser, then we have to add this resolver
+<p>Remember to add this resolver definition before the parser, then we have to add this resolver
 to our connect system, do that by updating the call to the <code>connect-plugin</code>, here is the updated
 parser:</p>
 </div>
@@ -4781,7 +4792,7 @@ parser:</p>
 </div>
 </div>
 <div class="paragraph">
-<p>Now we can run a query on <code>:demo-repos</code> like <code>[{:demo-repos [:github.repository/created-at]}]</code>, and walk the graph
+<p>Now we can run a query on <code>:demo-repos</code> like <code>[{:demo-repos [:github.Repository/createdAt]}]</code>, and walk the graph
 from there to anywhere allowed!</p>
 </div>
 </div>
@@ -4794,14 +4805,14 @@ GraphQL schema for your API.  The EDN queries in Fulcro might look like this:</p
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="clojure">(fp/defsc Repository
-  [this {<span class="symbol">:github.repository/keys</span> [id name-with-owner viewer-has-starred]}]
-  {<span class="symbol">:ident</span>         [<span class="symbol">:github.repository/id</span> <span class="symbol">:github.repository/id</span>]
-   <span class="symbol">:query</span>         [<span class="symbol">:github.repository/id</span> <span class="symbol">:github.repository/name-with-owner</span> <span class="symbol">:github.repository/viewer-has-starred</span>]}
+  [this {<span class="symbol">:github.Repository/keys</span> [id nameWithOwner viewerHasStarred]}]
+  {<span class="symbol">:ident</span> [<span class="symbol">:github.Repository/id</span> <span class="symbol">:github.Repository/id</span>]
+   <span class="symbol">:query</span> [<span class="symbol">:github.Repository/id</span> <span class="symbol">:github.Repository/nameWithOwner</span> <span class="symbol">:github.Repository/viewerHasStarred</span>]}
   <span class="keyword">..</span><span class="keyword">.</span>)
 
 (fp/defsc GraphqlDemo
   [this {<span class="symbol">:keys</span> [demo-repos]}]
-  {<span class="symbol">:query</span>         [{<span class="symbol">:demo-repos</span> (fp/get-query Repository)}]}
+  {<span class="symbol">:query</span> [{<span class="symbol">:demo-repos</span> (fp/get-query Repository)}]}
   (dom/div
     (mapv repository demo-repos)))</code></pre>
 </div>
@@ -4819,7 +4830,7 @@ simply prefix the mutation name with your GraphQL <code>prefix</code> and it&#82
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="clojure">(fm/defmutation github/add-star [_]
+<pre class="CodeRay highlight"><code data-lang="clojure">(fm/defmutation github/addStar [_]
   (action [{<span class="symbol">:keys</span> [state <span class="keyword">ref</span>]}] <span class="keyword">..</span><span class="keyword">.</span>)
   (remote [_] <span class="predefined-constant">true</span>))</code></pre>
 </div>
@@ -4831,7 +4842,7 @@ simply prefix the mutation name with your GraphQL <code>prefix</code> and it&#82
 <div class="title">Important</div>
 </td>
 <td class="content">
-This is <strong>not</strong> the <code>defmutation</code> we showed earlier in the setup.  This is Fulcro&#8217;s <code>defmutation</code>.
+This is <strong>not</strong> the <code>defmutation</code> we showed earlier in the setup. This is Fulcro&#8217;s <code>defmutation</code>.
 </td>
 </tr>
 </table>
@@ -4958,7 +4969,7 @@ defining a helper function:</p>
     [com.wsscode.common.async-cljs <span class="symbol">:refer</span> [go-promise let-chan &lt;!p go-catch &lt;? &lt;?maybe]]
     [com.wsscode.pathom.book.util.local-storage <span class="symbol">:as</span> ls]
     [com.wsscode.pathom.connect <span class="symbol">:as</span> pc]
-    [com.wsscode.pathom.connect.graphql <span class="symbol">:as</span> pcg]
+    [com.wsscode.pathom.connect.graphql2 <span class="symbol">:as</span> pcg]
     [com.wsscode.pathom.core <span class="symbol">:as</span> p]
     [com.wsscode.pathom.diplomat.http <span class="symbol">:as</span> p.http]
     [com.wsscode.pathom.diplomat.http.fetch <span class="symbol">:as</span> p.http.fetch]
@@ -4976,21 +4987,21 @@ defining a helper function:</p>
 (<span class="keyword">defonce</span> <span class="function">indexes</span> (<span class="keyword">atom</span> {}))
 
 (pc/defresolver repositories [_ _]
-  {<span class="symbol">::pc/output</span> [{<span class="symbol">:demo-repos</span> [<span class="symbol">:github.user/login</span> <span class="symbol">:github.repository/name</span>]}]}
+  {<span class="symbol">::pc/output</span> [{<span class="symbol">:demo-repos</span> [<span class="symbol">:github.User/login</span> <span class="symbol">:github.Repository/name</span>]}]}
   {<span class="symbol">:demo-repos</span>
-   [{<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">pathom</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-inspect</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-css</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-spec</span><span class="delimiter">&quot;</span></span>}
-    {<span class="symbol">:github.user/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">thheller</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">shadow-cljs</span><span class="delimiter">&quot;</span></span>}]})
+   [{<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">wilkerlucio</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">pathom</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-inspect</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-incubator</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcrologic</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">fulcro-spec</span><span class="delimiter">&quot;</span></span>}
+    {<span class="symbol">:github.User/login</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">thheller</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.Repository/name</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">shadow-cljs</span><span class="delimiter">&quot;</span></span>}]})
 
 (<span class="keyword">def</span> <span class="function">github-gql</span>
   {<span class="symbol">::pcg/url</span>       (<span class="keyword">str</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">https://api.github.com/graphql?access_token=</span><span class="delimiter">&quot;</span></span> (ls/get <span class="symbol">:github-token</span>))
    <span class="symbol">::pcg/prefix</span>    <span class="string"><span class="delimiter">&quot;</span><span class="content">github</span><span class="delimiter">&quot;</span></span>
-   <span class="symbol">::pcg/ident-map</span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> [<span class="string"><span class="delimiter">&quot;</span><span class="content">User</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span>]}
-                    <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> [<span class="string"><span class="delimiter">&quot;</span><span class="content">User</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span>]
-                                  <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  [<span class="string"><span class="delimiter">&quot;</span><span class="content">Repository</span><span class="delimiter">&quot;</span></span> <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>]}}
+   <span class="symbol">::pcg/ident-map</span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>       {<span class="string"><span class="delimiter">&quot;</span><span class="content">login</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>}
+                    <span class="string"><span class="delimiter">&quot;</span><span class="content">repository</span><span class="delimiter">&quot;</span></span> {<span class="string"><span class="delimiter">&quot;</span><span class="content">owner</span><span class="delimiter">&quot;</span></span> <span class="symbol">:github.User/login</span>
+                                  <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>  <span class="symbol">:github.Repository/name</span>}}
    <span class="symbol">::p.http/driver</span> p.http.fetch/request-async})
 
 (<span class="keyword">def</span> <span class="function">parser</span>
@@ -5012,39 +5023,35 @@ defining a helper function:</p>
   (go-promise
     (&lt;? (pcg/load-index github-gql indexes))))
 
-(fm/defmutation github/add-star [_]
+(fm/defmutation github/addStar [_]
   (action [{<span class="symbol">:keys</span> [state <span class="keyword">ref</span>]}]
-    (<span class="keyword">swap!</span> state <span class="keyword">update-in</span> <span class="keyword">ref</span> <span class="keyword">assoc</span> <span class="symbol">:github.repository/viewer-has-starred</span> <span class="predefined-constant">true</span>))
+    (<span class="keyword">swap!</span> state <span class="keyword">update-in</span> <span class="keyword">ref</span> <span class="keyword">assoc</span> <span class="symbol">:github.Repository/viewerHasStarred</span> <span class="predefined-constant">true</span>))
   (remote [_] <span class="predefined-constant">true</span>))
 
-(fm/defmutation github/remove-star [_]
+(fm/defmutation github/removeStar [_]
   (action [{<span class="symbol">:keys</span> [state <span class="keyword">ref</span>]}]
-    (<span class="keyword">swap!</span> state <span class="keyword">update-in</span> <span class="keyword">ref</span> <span class="keyword">assoc</span> <span class="symbol">:github.repository/viewer-has-starred</span> <span class="predefined-constant">false</span>))
+    (<span class="keyword">swap!</span> state <span class="keyword">update-in</span> <span class="keyword">ref</span> <span class="keyword">assoc</span> <span class="symbol">:github.Repository/viewerHasStarred</span> <span class="predefined-constant">false</span>))
   (remote [_] <span class="predefined-constant">true</span>))
 
 (fp/defsc Repository
-  [this {<span class="symbol">:github.repository/keys</span> [id name-with-owner viewer-has-starred]}]
-  {<span class="symbol">:initial-state</span> (<span class="keyword">fn</span> [_]
-                    {})
-   <span class="symbol">:ident</span>         [<span class="symbol">:github.repository/id</span> <span class="symbol">:github.repository/id</span>]
-   <span class="symbol">:query</span>         [<span class="symbol">:github.repository/id</span> <span class="symbol">:github.repository/name-with-owner</span> <span class="symbol">:github.repository/viewer-has-starred</span>]
-   <span class="symbol">:css</span>           []
-   <span class="symbol">:css-include</span>   []}
+  [this {<span class="symbol">:github.Repository/keys</span> [id nameWithOwner viewerHasStarred]}]
+  {<span class="symbol">:ident</span> [<span class="symbol">:github.Repository/id</span> <span class="symbol">:github.Repository/id</span>]
+   <span class="symbol">:query</span> [<span class="symbol">:github.Repository/id</span> <span class="symbol">:github.Repository/nameWithOwner</span> <span class="symbol">:github.Repository/viewerHasStarred</span>]}
   (dom/div
-    (dom/div name-with-owner)
-    (<span class="keyword">if</span> viewer-has-starred
-      (dom/button {<span class="symbol">:onClick</span> #(fp/transact! this [`{(github/remove-star {<span class="symbol">:github/input</span> {<span class="symbol">:github/starrable-id</span> ~id}})
-                                                   [<span class="symbol">:client-mutation-id</span>
+    (dom/div (<span class="keyword">str</span> nameWithOwner))
+    (<span class="keyword">if</span> viewerHasStarred
+      (dom/button {<span class="symbol">:onClick</span> #(fp/transact! this [`{(github/removeStar {<span class="symbol">:github/input</span> {<span class="symbol">:github/starrableId</span> ~id}})
+                                                   [<span class="symbol">:clientMutationId</span>
                                                     {<span class="symbol">:starrable</span>
-                                                     [<span class="symbol">:viewer-has-starred</span>]}]}])}
+                                                     [<span class="symbol">:viewerHasStarred</span>]}]}])}
         <span class="string"><span class="delimiter">&quot;</span><span class="content">Remove star</span><span class="delimiter">&quot;</span></span>)
-      (dom/button {<span class="symbol">:onClick</span> #(fp/transact! this [`{(github/add-star {<span class="symbol">:github/input</span> {<span class="symbol">:github/starrable-id</span> ~id}})
-                                                   [<span class="symbol">:client-mutation-id</span>
+      (dom/button {<span class="symbol">:onClick</span> #(fp/transact! this [`{(github/addStar {<span class="symbol">:github/input</span> {<span class="symbol">:github/starrableId</span> ~id}})
+                                                   [<span class="symbol">:clientMutationId</span>
                                                     {<span class="symbol">:starrable</span>
-                                                     [<span class="symbol">:viewer-has-starred</span>]}]}])}
+                                                     [<span class="symbol">:viewerHasStarred</span>]}]}])}
         <span class="string"><span class="delimiter">&quot;</span><span class="content">Add star</span><span class="delimiter">&quot;</span></span>))))
 
-(<span class="keyword">def</span> <span class="function">repository</span> (fp/factory Repository {<span class="symbol">:keyfn</span> <span class="symbol">:github.repository/id</span>}))
+(<span class="keyword">def</span> <span class="function">repository</span> (fp/factory Repository {<span class="symbol">:keyfn</span> <span class="symbol">:github.Repository/id</span>}))
 
 (fp/defsc GraphqlDemo
   [this {<span class="symbol">:keys</span> [demo-repos]}]
@@ -5074,7 +5081,7 @@ defining a helper function:</p>
                       <span class="symbol">:networking</span>
                       {<span class="symbol">:remote</span> (<span class="keyword">-&gt;</span> parser
                                    (pfn/pathom-remote)
-                                   (pfn/profile-remote))}}}))
+                                   (pfn/trace-remote))}}}))
 
 <span class="comment">; creates a parser view using pathom viz to explore the graph in workspaces</span>
 (ws/defcard graphql-demo-parser
@@ -5118,7 +5125,7 @@ and see the GraphQL equivalent been generated on the right.</p>
 <div id="footer">
 <div id="footer-text">
 Version 2<br>
-Last updated 2019-02-21 15:10:00 -03
+Last updated 2019-03-06 07:31:42 -03
 </div>
 </div>
 </body>
diff --git a/project.clj b/project.clj
index 7d82ddcf..5c94d046 100644
--- a/project.clj
+++ b/project.clj
@@ -1,4 +1,4 @@
-(defproject com.wsscode/pathom "2.2.11"
+(defproject com.wsscode/pathom "2.2.12"
   :description "A Clojure library designed to provide a collection of helper functions to support Clojure(script) graph parsers using\nom.next graph syntax."
   :url "https://github.com/wilkerlucio/pathom"
   :license {:name "MIT"
diff --git a/src/com/wsscode/pathom/connect/graphql.cljc b/src/com/wsscode/pathom/connect/graphql.cljc
index b2ee7cc3..03e88542 100644
--- a/src/com/wsscode/pathom/connect/graphql.cljc
+++ b/src/com/wsscode/pathom/connect/graphql.cljc
@@ -1,6 +1,10 @@
 (ns com.wsscode.pathom.connect.graphql
-  (:require [camel-snake-kebab.core :as csk]
-            [clojure.spec.alpha :as s]
+  "DEPRECATED: this whole namespaced is deprecated, it will stay in the codebase to keep
+  compatibility for old users, but we recommend moving to the graphql2 ns, the issues
+  that happen is that by trying to normalize the graphql names to be more clojure friendly
+  we end up in cases were graphql things get unreachable, the new approach just sends
+  the data as-is, making everything reachable"
+  (:require [clojure.spec.alpha :as s]
             [clojure.string :as str]
             [#?(:clj  com.wsscode.common.async-clj
                 :cljs com.wsscode.common.async-cljs) :refer [let-chan go-catch <? <?maybe]]
@@ -12,7 +16,8 @@
 
 (declare graphql-resolve graphql-mutation)
 
-(s/def ::ident-map (s/map-of string? (s/map-of string? (s/tuple string? string?))))
+(s/def ::ident-map (s/map-of string? (s/map-of string? (s/or :kw keyword?
+                                                             :tuple (s/tuple string? string?)))))
 (s/def ::resolver ::pc/sym)
 (s/def ::prefix string?)
 
diff --git a/src/com/wsscode/pathom/connect/graphql2.cljc b/src/com/wsscode/pathom/connect/graphql2.cljc
new file mode 100644
index 00000000..c02a1f11
--- /dev/null
+++ b/src/com/wsscode/pathom/connect/graphql2.cljc
@@ -0,0 +1,439 @@
+(ns com.wsscode.pathom.connect.graphql2
+  (:require [clojure.spec.alpha :as s]
+            [clojure.string :as str]
+            [#?(:clj  com.wsscode.common.async-clj
+                :cljs com.wsscode.common.async-cljs) :refer [let-chan go-catch <? <?maybe]]
+            [com.wsscode.pathom.core :as p]
+            [com.wsscode.pathom.connect :as pc]
+            [com.wsscode.pathom.graphql :as pg]
+            [com.wsscode.pathom.diplomat.http :as p.http]
+            [clojure.walk :as walk]))
+
+(declare graphql-resolve graphql-mutation)
+
+(s/def ::ident-map (s/map-of string? (s/map-of string? (s/or :kw keyword?
+                                                             :tuple (s/tuple string? string?)))))
+(s/def ::resolver ::pc/sym)
+(s/def ::prefix string?)
+
+(def schema-query
+  [{:__schema
+    [{:queryType
+      [:name]}
+
+     {:mutationType
+      [:name]}
+
+     {:types
+      [:name
+       :kind
+       {:interfaces [:name :kind]}
+       {:fields
+        [:name
+         {:args [:name :defaultValue {:type [:kind :name {:ofType 3}]}]}
+         {:type [:kind :name {:ofType 3}]}]}]}]}])
+
+(s/def ::mung (s/fspec :args (s/cat :string string?) :ret string?))
+(s/def ::demung (s/fspec :args (s/cat :string string?) :ret string?))
+
+(defn prefixed-key [{::keys [prefix mung]} p s] (keyword (str prefix "." p) (mung s)))
+(defn type-key [env s] (prefixed-key env "types" s))
+(defn interface-key [env s] (prefixed-key env "interfaces" s))
+(defn mutation-key [{::keys [prefix mung]} s] (symbol prefix (mung s)))
+(defn service-resolver-key [env] (mutation-key env "resolver"))
+(defn service-mutation-key [{::keys [prefix]}] (symbol "com.wsscode.pathom.connect.graphql.service-mutations" prefix))
+
+(defn type->field-entry [env {:keys [kind name ofType]}]
+  (case kind
+    "NON_NULL" (recur env ofType)
+    "LIST" (recur env ofType)
+    "OBJECT" {(type-key env name) {}}
+    "INTERFACE" {(interface-key env name) {}}
+    {}))
+
+(defn index-type-key [env {:keys [name kind]}]
+  (let [key-fun (case kind
+                  "OBJECT" type-key
+                  "INTERFACE" interface-key)]
+    (key-fun env name)))
+
+(defn entity-field-key [{::keys [mung prefix]} entity field]
+  (keyword (str prefix "." (mung entity)) (mung field)))
+
+(defn index-type [env {:keys [fields name interfaces] :as input}]
+  {#{(index-type-key env input)}
+   (-> {}
+       ; fields
+       (into (map #(vector (entity-field-key env name (:name %))
+                     (type->field-entry env (:type %)))) fields)
+       ; interfaces
+       (into (map #(vector (interface-key env (:name %)) {}) interfaces)))})
+
+(defn ident-root? [{::keys [ident-map]} {:keys [args]}]
+  (and (= 1 (count args))
+       (get ident-map (-> args first :name))))
+
+(defn ident-root [{::keys [prefix ident-map]} {:keys [name] :as root-field}]
+  (if-let [fields (get ident-map name)]
+    (if (= 1 (count fields))
+      (let [[entity field] (first (vals fields))]
+        (assoc root-field ::entity-field (entity-field-key prefix entity field))))))
+
+(defn ident-map-entry [env item]
+  (cond
+    (keyword? item) item
+    (vector? item) (entity-field-key env (first item) (second item))))
+
+(defn ident-map-params->io [env params]
+  (->> params vals (into #{} (map (fn [item] (ident-map-entry env item))))))
+
+(defn index-schema-io [{::keys [prefix schema ident-map mung] :as input}]
+  (let [schema (:__schema schema)]
+    (-> {}
+        (into (comp (filter (comp #{"OBJECT" "INTERFACE"} :kind))
+                    (map (partial index-type input)))
+              (:types schema))
+        (assoc #{} (into {} (map #(vector (keyword prefix (mung (:name %)))
+                                    (type->field-entry input (:type %))))
+                         (->> schema :queryType :fields)))
+        (as-> <>
+          (reduce (fn [idx {:keys [name type]}]
+                    (let [params    (get ident-map name)
+                          input-set (ident-map-params->io input params)]
+                      (update idx input-set pc/merge-io {(ffirst (type->field-entry input type)) {}})))
+            <>
+            (->> schema :queryType :fields
+                 (filter (comp ident-map :name))))))))
+
+(defn args-translate [{::keys [prefix ident-map]} args]
+  (or
+    (if-let [item (get ident-map (-> args first :name))]
+      (if (= 1 (count args))
+        #{(keyword (ident-map-entry prefix item))}))
+    #{}))
+
+(defn index-schema-oir [{::keys    [prefix schema resolver ident-map mung]
+                         ::pc/keys [index-io]
+                         :as       input}]
+  (let [schema (:__schema schema)
+        roots  (-> schema :queryType :fields)]
+    (-> {}
+        (into (map #(vector (keyword prefix (mung (:name %)))
+                      {(args-translate input (:args %)) #{resolver}}))
+              roots)
+        (into (comp
+                (filter (comp ident-map :name))
+                (mapcat (fn [{:keys [type name]}]
+                          (let [params    (get ident-map name)
+                                input-set (ident-map-params->io input params)
+                                fields    (-> (get index-io #{(ffirst (type->field-entry input type))}) keys)]
+                            (mapv (fn [field]
+                                    [field {input-set #{resolver}}])
+                              fields)))))
+              roots))))
+
+(defn index-autocomplete-ignore [{::keys [schema] :as env}]
+  (let [schema (:__schema schema)]
+    (-> #{}
+        (into (comp (filter (comp #{"OBJECT" "INTERFACE"} :kind))
+                    (map (partial index-type-key env)))
+              (:types schema)))))
+
+(defn index-idents [{::keys [ident-map] :as env}]
+  (into #{}
+        (map #(ident-map-entry env %))
+        (->> (vals ident-map)
+             (filterv #(= 1 (count %)))
+             (map vals)
+             (apply concat))))
+
+(defn index-graphql-idents [{::keys    [schema ident-map mung]
+                             ::pc/keys [index-io]
+                             :as       env}]
+  (let [schema (:__schema schema)
+        fields (-> schema :queryType :fields)
+        idents (filter (comp ident-map :name) fields)]
+    (-> {}
+        (into (mapcat (fn [{:keys [name type]}]
+                        (let [params        (get ident-map name)
+                              ident-key     (keyword (mung name) (mung (str/join "-and-" (keys params))))
+                              entity-fields (mapv (fn [item] (ident-map-entry env item)) (vals params))
+                              entity-field  (cond-> entity-fields (= 1 (count entity-fields)) first)
+                              fields        (-> (get index-io #{(ffirst (type->field-entry env type))})
+                                                keys)]
+                          (mapv (fn [field]
+                                  [field {::entity-field entity-field
+                                          ::ident-key    ident-key}])
+                            fields))))
+              idents))))
+
+(defn index-mutations [{::keys [schema] :as config}]
+  (let [mutations (-> schema :__schema :mutationType :fields)]
+    (into
+      {(service-mutation-key config)
+       (pc/mutation (service-mutation-key config) {}
+         (fn [env _] (graphql-mutation config env)))}
+      (map (fn [{:keys [name]}]
+             [(mutation-key config name) {::pc/sym (service-mutation-key config)}]))
+      mutations)))
+
+(defn index-schema-types [schema]
+  (let [index (->> schema :__schema :types
+                   (group-by :name)
+                   (into {} (map (fn [[k v]] [k (first v)]))))]
+    (-> (assoc schema ::types-index index)
+        (update-in [:__schema :queryType] #(merge % (get index (:name %))))
+        (update-in [:__schema :mutationType] #(merge % (get index (:name %)))))))
+
+(defn remove-pathom-params [params]
+  (if params
+    (reduce-kv
+      (fn [m k v]
+        (if (and (keyword? k)
+                 (= "pathom" (namespace k)))
+          m
+          (assoc m k v)))
+      {}
+      params)))
+
+(defn filter-graphql-subquery [{::p/keys [parent-query]
+                                ::keys   [prefix]
+                                :as      env}]
+  (let [ent (p/entity env)]
+    (->> parent-query
+         (p/lift-placeholders env)
+         p/query->ast
+         (p/transduce-children
+           (comp (filter #(str/starts-with? (or (namespace (:dispatch-key %)) "") prefix))
+                 (map #(update % :params remove-pathom-params))))
+         :children
+         ; remove already known keys
+         (remove #(contains? ent (:key %)))
+         ; remove ident attributes
+         (remove (comp vector? :key)))))
+
+(defn index-schema [{::keys [resolver prefix] :as config}]
+  (let [config   (merge {::mung identity} config)
+        resolver (or resolver (service-resolver-key config))
+        config   (update config ::schema index-schema-types)
+        index-io (index-schema-io config)
+        config   (assoc config ::pc/index-io index-io
+                               ::resolver resolver)]
+    {::pc/index-resolvers
+     {resolver {::pc/sym            resolver
+                ::pc/cache?         false
+                ::pc/compute-output (fn [env]
+                                      (->> (filter-graphql-subquery (assoc env ::prefix prefix))
+                                           (hash-map :type :root :children)
+                                           p/ast->query))
+                ::graphql?          true
+                ::pc/resolve        (fn [env _] (graphql-resolve config env))}}
+
+     ::pc/index-io
+     index-io
+
+     ::pc/index-oir
+     (index-schema-oir config)
+
+     ::pc/autocomplete-ignore
+     (index-autocomplete-ignore config)
+
+     ::pc/idents
+     (index-idents config)
+
+     ::pc/index-mutations
+     (index-mutations config)
+
+     ::field->ident
+     (index-graphql-idents config)}))
+
+;;;; resolver
+
+(s/fdef index-schema
+  :args (s/cat :input (s/keys :req [::schema ::prefix] :opt [::resolver ::ident-map ::mung]))
+  :ret  (s/merge ::pc/indexes
+          (s/keys :req [::pc/autocomplete-ignore ::field->ident])))
+
+(defn demung-key [demung s]
+  (if (vector? s)
+    s
+    (keyword (demung (name s)))))
+
+(defn gql-ident-reader [{:keys [ast]
+                         :as   env}]
+  (if (vector? (:key ast))
+    (let [e (p/entity env)]
+      (let [json (get e (keyword (pg/ident->alias (:key ast))))]
+        (p/join json env)))
+    ::p/continue))
+
+(defn index-graphql-errors [errors]
+  (->> errors
+       (map (fn [{:keys [path] :as err}]
+              (cond-> err
+                (and path (contains? #{"query" "mutation"} (first path)))
+                (update :path (comp vec next)))))
+       (group-by :path)))
+
+(defn error-stamper [{::keys   [errors base-path demung]
+                      ::p/keys [path errors*]}]
+  (let [path' (mapv #(cond
+                       (p/ident? %)
+                       (demung (namespace (first %)))
+
+                       (keyword? %)
+                       (demung (name %))
+
+                       :else %)
+                path)]
+    (if-let [local-errors (or (get errors path')
+                              (get errors nil))]
+      (do
+        (if errors*
+          (swap! errors* assoc (into base-path (remove p/ident? path)) (first local-errors)))
+        ::p/reader-error)
+      ::p/continue)))
+
+(defn alias-for-line [query line]
+  (try
+    (if-let [[_ alias] (-> (str/split-lines query)
+                           (nth (dec line))
+                           (->> (re-find #"^\s*([^:]+): \w+")))]
+      alias)
+    (catch #?(:clj Throwable :cljs :default) _ nil)))
+
+(defn demunger-map-reader
+  "Reader that will demunge keys using user function"
+  [{::keys [demung]
+    :keys  [ast query]
+    :as    env}]
+  (let [entity (p/entity env)]
+    (if-let [[_ v] (find entity (demung-key demung (:key ast)))]
+      (if (sequential? v)
+        (if query
+          (p/join-seq env v)
+          v)
+        (if (and (map? v) query)
+          (p/join v env)
+          v))
+      ::p/continue)))
+
+(def parser-item
+  (p/parser {::p/env     {::p/reader [error-stamper
+                                      demunger-map-reader
+                                      p/env-placeholder-reader
+                                      gql-ident-reader]}
+             ::p/plugins [(p/env-wrap-plugin
+                            (fn [env]
+                              (-> (merge {::demung identity} env)
+                                  (update ::p/placeholder-prefixes
+                                    #(or % #{})))))]}))
+
+(defn query->graphql
+  "Like the pg/query-graphql, but adds name convertion so clj names like :first-name turns in firstName."
+  [query {::keys [demung] :or {demung identity}}]
+  (pg/query->graphql query {::pg/js-name (comp demung name)}))
+
+(defn ast->graphql [{:keys     [ast]
+                     ::pc/keys [indexes]}
+                    ent]
+  (let [{::keys [field->ident]} indexes
+        {:keys [key]} ast
+        q [(p/ast->query ast)]]
+    (if-let [{::keys [entity-field ident-key]} (get field->ident key)]
+      (let [ident-value (if (vector? entity-field) (mapv ent entity-field) (get ent entity-field))
+            ident-key'  [ident-key ident-value]]
+        [{ident-key' q}])
+      q)))
+
+(defn build-query [env]
+  (->> (filter-graphql-subquery env)
+       (map #(ast->graphql (assoc env :ast %) (p/entity env)))
+       (reduce p/merge-queries [])))
+
+(defn pull-idents [data]
+  (reduce-kv (fn [x k v]
+               (if (vector? k)
+                 (into x v)
+                 (assoc x k v)))
+    {}
+    data))
+
+(defn request [{::keys [url] :as env} query]
+  (let-chan [response (p.http/request (assoc env ::p.http/url url
+                                                 ::p.http/content-type ::p.http/json
+                                                 ::p.http/method ::p.http/post
+                                                 ::p.http/as ::p.http/json
+                                                 ::p.http/form-params {:query (if (string? query) query (query->graphql query env))}))]
+    (::p.http/body response)))
+
+(defn normalize-schema
+  "Depending on encoding settings sometimes the :kind can come as a keyword, the indexer expects it to
+  be a string, this function ensures all :kind fields are strings."
+  [schema]
+  (walk/postwalk
+    (fn [x]
+      (if (and (map? x)
+               (contains? x :kind))
+        (update x :kind #(some-> % name))
+        x))
+    schema))
+
+(defn load-index
+  ([req]
+   (let-chan [{:keys [data]} (request req (pg/query->graphql schema-query req))]
+     (index-schema (assoc req ::schema (normalize-schema data)))))
+  ([req indexes]
+   (let-chan [idx (load-index req)]
+     (swap! indexes pc/merge-indexes idx))))
+
+(defn graphql-resolve [{::keys [demung] :as config} env]
+  (let [env' (merge env config)
+        q    (build-query env')
+        gq   (query->graphql q config)]
+    (let-chan [{:keys [data errors]} (request env' gq)]
+      (-> (parser-item {::p/entity               data
+                        ::p/errors*              (::p/errors* env)
+                        ::p/placeholder-prefixes (::p/placeholder-prefixes env')
+                        ::demung                 (or demung identity)
+                        ::base-path              (vec (butlast (::p/path env)))
+                        ::graphql-query          gq
+                        ::errors                 (index-graphql-errors errors)}
+            q)
+          (pull-idents)))))
+
+(defn graphql-mutation [{::keys [demung] :as config} env]
+  (let [{:keys     [ast]
+         ::pc/keys [source-mutation]
+         :as       env'} (merge env config)
+        query (p/ast->query {:type :root :children [(assoc ast :key source-mutation :dispatch-key source-mutation)]})
+        gq    (query->graphql query config)]
+    (let-chan [{:keys [data errors]} (request env' gq)]
+      #_ (js/console.log "Mutation response" data errors env config)
+      (let [parser-response
+            (-> (parser-item {::p/entity      data
+                              ::p/errors*     (::p/errors* env)
+                              ::base-path     (vec (butlast (::p/path env)))
+                              ::demung        (or demung identity)
+                              ::graphql-query gq
+                              ::errors        (index-graphql-errors errors)}
+                  (p/ast->query {:type     :root
+                                 :children [(assoc ast
+                                              :type :join
+                                              :key (keyword source-mutation)
+                                              :dispatch-key (keyword source-mutation))]})))]
+        (get parser-response (keyword source-mutation))))))
+
+(defn defgraphql-resolver [{::pc/keys [resolver-dispatch mutate-dispatch]} {::keys [resolver] :as config}]
+  (let [config (merge {::demung identity} config)]
+    (if resolver-dispatch
+      (defmethod resolver-dispatch resolver [env _]
+        (graphql-resolve config env)))
+
+    (if mutate-dispatch
+      (defmethod mutate-dispatch (service-mutation-key config) [env _]
+        (graphql-mutation config env)))))
+
+(s/fdef defgraphql-resolver
+  :args (s/cat :env (s/keys :opt [::pc/resolver-dispatch ::pc/mutate-dispatch])
+               :config (s/keys :req [::resolver ::prefix])))
diff --git a/src/com/wsscode/pathom/fulcro/network.cljs b/src/com/wsscode/pathom/fulcro/network.cljs
index baebd865..6d08eeec 100644
--- a/src/com/wsscode/pathom/fulcro/network.cljs
+++ b/src/com/wsscode/pathom/fulcro/network.cljs
@@ -211,6 +211,39 @@
                   (catch :default e
                     (error e))))))))
 
+(def graphql-response-parser2
+  (let [simple-keyword (comp keyword name)]
+    (p/parser {::p/env    {::p/reader (p/map-reader* {::p/map-key-transform simple-keyword})}
+               ::p/mutate (fn [env k _]
+                            {:action
+                             (fn []
+                               (let [response (-> (p/entity env) (get (simple-keyword k)))
+                                     id-param (pg/find-id (get-in env [:ast :params]) fp/tempid?)]
+                                 (js/console.log "VOLTA" id-param (p/entity env) response)
+                                 (cond-> response
+                                   id-param (assoc ::fp/tempids {(val id-param) (get response (simple-keyword (key id-param)))}))))})})))
+
+(defn graphql-network2
+  ([url] (graphql-network2 url {}))
+  ([url config]
+   (fn-network
+     (fn [this edn ok error]
+       (go
+         (try
+           (let [edn      (-> edn
+                              p/query->ast
+                              (p/elide-ast-nodes #{::pp/profile})
+                              p/ast->query)
+                 query    (pg/query->graphql edn (merge {::pg/tempid? fp/tempid?} config))
+                 response (<? (fetch/request-async {::http/url         url
+                                                    ::http/method      ::http/post
+                                                    ::http/as          ::http/json
+                                                    ::http/form-params {:query query}}))
+                 {:keys [data errors]} (::http/body response)]
+             (ok (graphql-response-parser2 {::p/entity data} edn)))
+           (catch :default e
+             (error e))))))))
+
 ;; Batch Networking
 
 (defn debounce
diff --git a/workspaces/src/com/wsscode/pathom/workspaces/graphql/github_demo.cljs b/workspaces/src/com/wsscode/pathom/workspaces/graphql/github_demo.cljs
index dc91832d..2d628f81 100644
--- a/workspaces/src/com/wsscode/pathom/workspaces/graphql/github_demo.cljs
+++ b/workspaces/src/com/wsscode/pathom/workspaces/graphql/github_demo.cljs
@@ -3,7 +3,7 @@
     [com.wsscode.common.async-cljs :refer [go-promise let-chan <!p go-catch <? <?maybe]]
     [com.wsscode.pathom.book.util.local-storage :as ls]
     [com.wsscode.pathom.connect :as pc]
-    [com.wsscode.pathom.connect.graphql :as pcg]
+    [com.wsscode.pathom.connect.graphql2 :as pcg]
     [com.wsscode.pathom.core :as p]
     [com.wsscode.pathom.diplomat.http :as p.http]
     [com.wsscode.pathom.diplomat.http.fetch :as p.http.fetch]
@@ -21,21 +21,21 @@
 (defonce indexes (atom {}))
 
 (pc/defresolver repositories [_ _]
-  {::pc/output [{:demo-repos [:github.user/login :github.repository/name]}]}
+  {::pc/output [{:demo-repos [:github.User/login :github.Repository/name]}]}
   {:demo-repos
-   [{:github.user/login "wilkerlucio" :github.repository/name "pathom"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-inspect"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-css"}
-    {:github.user/login "fulcrologic" :github.repository/name "fulcro-spec"}
-    {:github.user/login "thheller" :github.repository/name "shadow-cljs"}]})
+   [{:github.User/login "wilkerlucio" :github.Repository/name "pathom"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-inspect"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-incubator"}
+    {:github.User/login "fulcrologic" :github.Repository/name "fulcro-spec"}
+    {:github.User/login "thheller" :github.Repository/name "shadow-cljs"}]})
 
 (def github-gql
   {::pcg/url       (str "https://api.github.com/graphql?access_token=" (ls/get :github-token))
    ::pcg/prefix    "github"
-   ::pcg/ident-map {"user"       {"login" ["User" "login"]}
-                    "repository" {"owner" ["User" "login"]
-                                  "name"  ["Repository" "name"]}}
+   ::pcg/ident-map {"user"       {"login" :github.User/login}
+                    "repository" {"owner" :github.User/login
+                                  "name"  :github.Repository/name}}
    ::p.http/driver p.http.fetch/request-async})
 
 (def parser
@@ -57,39 +57,35 @@
   (go-promise
     (<? (pcg/load-index github-gql indexes))))
 
-(fm/defmutation github/add-star [_]
+(fm/defmutation github/addStar [_]
   (action [{:keys [state ref]}]
-    (swap! state update-in ref assoc :github.repository/viewer-has-starred true))
+    (swap! state update-in ref assoc :github.Repository/viewerHasStarred true))
   (remote [_] true))
 
-(fm/defmutation github/remove-star [_]
+(fm/defmutation github/removeStar [_]
   (action [{:keys [state ref]}]
-    (swap! state update-in ref assoc :github.repository/viewer-has-starred false))
+    (swap! state update-in ref assoc :github.Repository/viewerHasStarred false))
   (remote [_] true))
 
 (fp/defsc Repository
-  [this {:github.repository/keys [id name-with-owner viewer-has-starred]}]
-  {:initial-state (fn [_]
-                    {})
-   :ident         [:github.repository/id :github.repository/id]
-   :query         [:github.repository/id :github.repository/name-with-owner :github.repository/viewer-has-starred]
-   :css           []
-   :css-include   []}
+  [this {:github.Repository/keys [id nameWithOwner viewerHasStarred]}]
+  {:ident [:github.Repository/id :github.Repository/id]
+   :query [:github.Repository/id :github.Repository/nameWithOwner :github.Repository/viewerHasStarred]}
   (dom/div
-    (dom/div name-with-owner)
-    (if viewer-has-starred
-      (dom/button {:onClick #(fp/transact! this [`{(github/remove-star {:github/input {:github/starrable-id ~id}})
-                                                   [:client-mutation-id
+    (dom/div (str nameWithOwner))
+    (if viewerHasStarred
+      (dom/button {:onClick #(fp/transact! this [`{(github/removeStar {:github/input {:github/starrableId ~id}})
+                                                   [:clientMutationId
                                                     {:starrable
-                                                     [:viewer-has-starred]}]}])}
+                                                     [:viewerHasStarred]}]}])}
         "Remove star")
-      (dom/button {:onClick #(fp/transact! this [`{(github/add-star {:github/input {:github/starrable-id ~id}})
-                                                   [:client-mutation-id
+      (dom/button {:onClick #(fp/transact! this [`{(github/addStar {:github/input {:github/starrableId ~id}})
+                                                   [:clientMutationId
                                                     {:starrable
-                                                     [:viewer-has-starred]}]}])}
+                                                     [:viewerHasStarred]}]}])}
         "Add star"))))
 
-(def repository (fp/factory Repository {:keyfn :github.repository/id}))
+(def repository (fp/factory Repository {:keyfn :github.Repository/id}))
 
 (fp/defsc GraphqlDemo
   [this {:keys [demo-repos]}]
@@ -119,7 +115,7 @@
                       :networking
                       {:remote (-> parser
                                    (pfn/pathom-remote)
-                                   (pfn/profile-remote))}}}))
+                                   (pfn/trace-remote))}}}))
 
 ; creates a parser view using pathom viz to explore the graph in workspaces
 (ws/defcard graphql-demo-parser
diff --git a/workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs b/workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs
index a92a6221..d64cbd43 100644
--- a/workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs
+++ b/workspaces/src/com/wsscode/pathom/workspaces/graphql/simple_todo_demo.cljs
@@ -12,16 +12,16 @@
 
 (declare TodoItem)
 
-(fm/defmutation update-todo-item [todo]
+(fm/defmutation updateTodoItem [todo]
   (action [{:keys [state ref]}]
     (swap! state update-in ref merge todo))
   (remote [{:keys [ast state]}]
     (-> ast
         (fm/returning state TodoItem))))
 
-(fm/defmutation delete-todo-item [{:todo/keys [id]}]
+(fm/defmutation deleteTodoItem [{:todo/keys [id]}]
   (action [env]
-    (db.h/swap-entity! env update :all-todo-items #(into [] (remove (comp #{id} second)) %)))
+    (db.h/swap-entity! env update :allTodoItems #(into [] (remove (comp #{id} second)) %)))
   (remote [{:keys [ast state]}]
     (-> ast
         (update :params select-keys [:todo/id])
@@ -45,7 +45,7 @@
       (rk/input {:type        "checkbox"
                  :checked     completed
                  :marginRight 5
-                 :onChange    #(fp/transact! this [`(update-todo-item ~{:todo/id id :todo/completed (not completed)})])})
+                 :onChange    #(fp/transact! this [`(updateTodoItem ~{:todo/id id :todo/completed (not completed)})])})
       (str title))
     (rk/inline-block {:cursor  "pointer"
                       :onClick on-delete-todo}
@@ -64,16 +64,17 @@
    :css           []
    :css-include   []}
   (rk/group {:marginBottom 10}
-    (rk/input {:type     "text"
-               :value    title
-               :onChange #(let [value (.. % -target -value)] (js/setTimeout (fn [] (fm/set-value! this :todo/title value)) 300))})
+    (with-redefs [fulcro.client.dom/form-elements? (fn [_] false)]
+      (rk/input {:type     "text"
+                 :value    title
+                 :onChange #(let [value (.. % -target -value)] (fm/set-value! this :todo/title value))}))
     (rk/button {:onClick #(on-save-todo (fp/props this))}
       "Add"
       (fa/plus-square))))
 
-(fm/defmutation create-todo-item [todo]
+(fm/defmutation createTodoItem [todo]
   (action [env]
-    (db.h/swap-entity! env update :all-todo-items conj (fp/get-ident TodoItem todo))
+    (db.h/swap-entity! env update :allTodoItems conj (fp/get-ident TodoItem todo))
     (db.h/create-entity! env NewTodo {} :replace :ui/new-todo))
   (remote [{:keys [ast]}]
     (update ast :params select-keys [:todo/id :todo/title])))
@@ -81,25 +82,25 @@
 (def new-todo-ui (fp/factory NewTodo {:keyfn :todo/id}))
 
 (fp/defsc TodoSimpleDemo
-  [this {:keys [all-todo-items] :ui/keys [new-todo]}]
+  [this {:keys [allTodoItems] :ui/keys [new-todo]}]
   {:initial-state (fn [_]
                     {:ui/new-todo (fp/get-initial-state NewTodo {})})
    :ident         (fn [] [::root "singleton"])
    :query         [{:ui/new-todo (fp/get-query NewTodo)}
-                   {:all-todo-items (fp/get-query TodoItem)}]
+                   {:allTodoItems (fp/get-query TodoItem)}]
    :css           []
    :css-include   [TodoItem NewTodo]}
   (rk/block
-    (new-todo-ui (fp/computed new-todo {::on-save-todo #(fp/transact! this [`(create-todo-item ~%)])}))
-    (for [todo all-todo-items]
-      (todo-item (fp/computed todo {::on-delete-todo #(fp/transact! this [`(delete-todo-item ~todo)])})))))
+    (new-todo-ui (fp/computed new-todo {::on-save-todo #(fp/transact! this [`(createTodoItem ~%)])}))
+    (for [todo allTodoItems]
+      (todo-item (fp/computed todo {::on-delete-todo #(fp/transact! this [`(deleteTodoItem ~todo)])})))))
 
 (ws/defcard todo-simple-demo
   (ct.fulcro/fulcro-card
     {::f.portal/root TodoSimpleDemo
      ::f.portal/app  {:started-callback
                       (fn [app]
-                        (df/load app :all-todo-items TodoItem {:target [::root "singleton" :all-todo-items]}))
+                        (df/load app :allTodoItems TodoItem {:target [::root "singleton" :allTodoItems]}))
 
                       :networking
-                      {:remote (-> (pfn/graphql-network "https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk"))}}}))
+                      {:remote (-> (pfn/graphql-network2 "https://api.graph.cool/simple/v1/cjjkw3slu0ui40186ml4jocgk"))}}}))


Test patch:
diff --git a/test/com/wsscode/pathom/connect/graphql2_test.cljc b/test/com/wsscode/pathom/connect/graphql2_test.cljc
new file mode 100644
index 00000000..ab1f4798
--- /dev/null
+++ b/test/com/wsscode/pathom/connect/graphql2_test.cljc
@@ -0,0 +1,453 @@
+(ns com.wsscode.pathom.connect.graphql2-test
+  (:require [clojure.test :refer [is are testing]]
+            [nubank.workspaces.core :refer [deftest]]
+            [com.wsscode.pathom.core :as p]
+            [com.wsscode.pathom.connect :as pc]
+            [com.wsscode.pathom.connect.graphql2 :as pcg]
+            [com.wsscode.pathom.graphql :as pg]))
+
+(def query-root-type
+  (pcg/normalize-schema
+    {:name   "QueryRoot"
+     :fields [{:name "banks" :args [] :type {:kind "LIST" :name nil :ofType {:kind "OBJECT" :name "Bank"}}}
+              {:name "creditCardAccount"
+               :args [{:name "customerId" :defaultValue nil :type {:kind "SCALAR" :name "ID"}}]
+               :type {:kind "OBJECT" :name "CreditCardAccount" :ofType nil}}
+              {:name "customer"
+               :args [{:name "customerId" :defaultValue nil :type {:kind "SCALAR" :name "ID"}}]
+               :type {:kind :OBJECT :name "Customer" :ofType nil}}
+              {:name "repository"
+               :args [{:name "owner" :defaultValue nil :type {:kind "SCALAR" :name "String"}}
+                      {:name "name" :defaultValue nil :type {:kind "SCALAR" :name "String"}}]
+               :type {:kind "OBJECT" :name "Repository" :ofType nil}}
+              {:name "savingsAccount"
+               :args [{:name "customerId" :defaultValue nil :type {:kind "SCALAR" :name "ID"}}]
+               :type {:kind "OBJECT" :name "SavingsAccount" :ofType nil}}
+              {:name "viewer" :args [] :type {:kind "OBJECT" :name "Customer" :ofType nil}}]}))
+
+(def customer-type
+  {:name       "Customer"
+   :kind       "OBJECT"
+   :interfaces []
+   :fields     [{:name "id" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "ID"}}}
+                {:name "cpf" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}
+                {:name "creditCardAccount" :args [] :type {:kind "OBJECT" :name "CreditCardAccount" :ofType nil}}
+                {:name "feed" :args [] :type {:kind "LIST" :name nil :ofType {:kind "NON_NULL" :name nil :ofType {:kind "INTERFACE" :name "FeedEvent"}}}}
+                {:name "name" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}
+                {:name "preferredName" :args [] :type {:kind "SCALAR" :name "String" :ofType nil}}
+                {:name "savingsAccount" :args [] :type {:kind "OBJECT" :name "SavingsAccount" :ofType nil}}]})
+
+(def repository-type
+  {:name       "Repository"
+   :kind       "OBJECT"
+   :interfaces []
+   :fields     [{:name "id" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "ID"}}}
+                {:name "name" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}]})
+
+(def feed-event-interface
+  {:name       "FeedEvent"
+   :kind       "INTERFACE"
+   :interfaces []
+   :fields     [{:name "detail" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}
+                {:name "id" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "ID"}}}
+                {:name "postDate" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "Date"}}}
+                {:name "title" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}]})
+
+(def onboarding-event-type
+  {:name       "OnboardingEvent"
+   :kind       "OBJECT"
+   :interfaces [{:name "FeedEvent" :kind "INTERFACE"}]
+   :fields     [{:name "detail" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}
+                {:name "id" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "ID"}}}
+                {:name "postDate" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "Date"}}}
+                {:name "title" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}}}]})
+
+(def mutation-type
+  {:name   "Mutation"
+   :fields [{:name "addStar"}
+            {:name "removeStar"}
+            {:name "requestReviews"}]})
+
+(def types
+  [{:name       "CreditCardBalances"
+    :kind       "OBJECT"
+    :interfaces []
+    :fields     [{:name "available" :args [] :type {:kind "SCALAR" :name "Float" :ofType nil}}
+                 {:name "due" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "Float"}}}
+                 {:name "future" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "Float"}}}
+                 {:name "open" :args [] :type {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "Float"}}}
+                 {:name "prepaid" :args [] :type {:kind "SCALAR" :name "Float" :ofType nil}}]}
+   query-root-type
+   customer-type
+   repository-type
+   feed-event-interface
+   onboarding-event-type
+   mutation-type])
+
+(def schema
+  {:__schema
+   {:queryType    {:name "QueryRoot"}
+    :mutationType {:name "Mutation"}
+    :types        types}})
+
+(def prefix "service")
+(def env {::pcg/prefix "service" ::pcg/mung identity})
+
+(deftest test-type-key
+  (is (= (pcg/type-key env "CreditCardBalances")
+         :service.types/CreditCardBalances)))
+
+(deftest test-interface-key
+  (is (= (pcg/interface-key env "FeedEvent")
+         :service.interfaces/FeedEvent)))
+
+(deftest test-type->field-entry
+  (is (= (pcg/type->field-entry env {:kind "SCALAR" :name "Float" :ofType nil})
+         {}))
+  (is (= (pcg/type->field-entry env {:kind "OBJECT" :name "CreditCardAccount" :ofType nil})
+         {:service.types/CreditCardAccount {}}))
+  (is (= (pcg/type->field-entry env {:kind "INTERFACE" :name "FeedEvent" :ofType nil})
+         {:service.interfaces/FeedEvent {}}))
+  (is (= (pcg/type->field-entry env {:kind "NON_NULL" :name nil :ofType {:kind "SCALAR" :name "String"}})
+         {}))
+  (is (= (pcg/type->field-entry env {:kind "NON_NULL" :name nil :ofType {:kind "OBJECT" :name "CreditCardAccount" :ofType nil}})
+         {:service.types/CreditCardAccount {}}))
+  (is (= (pcg/type->field-entry env {:kind "LIST" :name nil :ofType {:kind "OBJECT" :name "Bank"}})
+         {:service.types/Bank {}})))
+
+(deftest test-index-type
+  (is (= (pcg/index-type env customer-type)
+         {#{:service.types/Customer} #:service.Customer{:cpf               {}
+                                                        :creditCardAccount #:service.types{:CreditCardAccount {}}
+                                                        :feed              #:service.interfaces{:FeedEvent {}}
+                                                        :id                {}
+                                                        :name              {}
+                                                        :preferredName     {}
+                                                        :savingsAccount    #:service.types{:SavingsAccount {}}}}))
+
+  (is (= (pcg/index-type env feed-event-interface)
+         {#{:service.interfaces/FeedEvent} #:service.FeedEvent{:detail   {}
+                                                               :id       {}
+                                                               :postDate {}
+                                                               :title    {}}}))
+
+  (is (= (pcg/index-type env onboarding-event-type)
+         {#{:service.types/OnboardingEvent} {:service.OnboardingEvent/detail   {}
+                                             :service.OnboardingEvent/id       {}
+                                             :service.OnboardingEvent/postDate {}
+                                             :service.OnboardingEvent/title    {}
+                                             :service.interfaces/FeedEvent     {}}})))
+
+(def supposed-resolver nil)
+
+(def indexes
+  `{:com.wsscode.pathom.connect.graphql2/field->ident {:service.Customer/cpf               #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/creditCardAccount #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/feed              #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/id                #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/name              #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/preferredName     #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Customer/savingsAccount    #:com.wsscode.pathom.connect.graphql2{:entity-field :service.Customer/id
+                                                                                                                                 :ident-key    :customer/customerId}
+                                                       :service.Repository/id              #:com.wsscode.pathom.connect.graphql2{:entity-field [:service.Customer/name
+                                                                                                                                                :service.Repository/name]
+                                                                                                                                 :ident-key    :repository/owner-and-name}
+                                                       :service.Repository/name            #:com.wsscode.pathom.connect.graphql2{:entity-field [:service.Customer/name
+                                                                                                                                                :service.Repository/name]
+                                                                                                                                 :ident-key    :repository/owner-and-name}}
+    :com.wsscode.pathom.connect/autocomplete-ignore   #{:service.interfaces/FeedEvent
+                                                        :service.types/CreditCardBalances
+                                                        :service.types/Customer
+                                                        :service.types/OnboardingEvent
+                                                        :service.types/Repository}
+    :com.wsscode.pathom.connect/idents                #{:service.Customer/id}
+    :com.wsscode.pathom.connect/index-io              {#{:service.Customer/id}              #:service.types{:CreditCardAccount {}
+                                                                                                            :Customer          {}
+                                                                                                            :SavingsAccount    {}}
+                                                       #{:service.Customer/name
+                                                         :service.Repository/name}          #:service.types{:Repository {}}
+                                                       #{:service.interfaces/FeedEvent}     #:service.FeedEvent{:detail   {}
+                                                                                                                :id       {}
+                                                                                                                :postDate {}
+                                                                                                                :title    {}}
+                                                       #{:service.types/CreditCardBalances} #:service.CreditCardBalances{:available {}
+                                                                                                                         :due       {}
+                                                                                                                         :future    {}
+                                                                                                                         :open      {}
+                                                                                                                         :prepaid   {}}
+                                                       #{:service.types/Customer}           #:service.Customer{:cpf               {}
+                                                                                                               :creditCardAccount #:service.types{:CreditCardAccount {}}
+                                                                                                               :feed              #:service.interfaces{:FeedEvent {}}
+                                                                                                               :id                {}
+                                                                                                               :name              {}
+                                                                                                               :preferredName     {}
+                                                                                                               :savingsAccount    #:service.types{:SavingsAccount {}}}
+                                                       #{:service.types/OnboardingEvent}    {:service.OnboardingEvent/detail   {}
+                                                                                             :service.OnboardingEvent/id       {}
+                                                                                             :service.OnboardingEvent/postDate {}
+                                                                                             :service.OnboardingEvent/title    {}
+                                                                                             :service.interfaces/FeedEvent     {}}
+                                                       #{:service.types/Repository}         #:service.Repository{:id   {}
+                                                                                                                 :name {}}
+                                                       #{}                                  #:service{:banks             #:service.types{:Bank {}}
+                                                                                                      :creditCardAccount #:service.types{:CreditCardAccount {}}
+                                                                                                      :customer          #:service.types{:Customer {}}
+                                                                                                      :repository        #:service.types{:Repository {}}
+                                                                                                      :savingsAccount    #:service.types{:SavingsAccount {}}
+                                                                                                      :viewer            #:service.types{:Customer {}}}}
+    :com.wsscode.pathom.connect/index-mutations       {com.wsscode.pathom.connect.graphql.service-mutations/service #:com.wsscode.pathom.connect{:sym com.wsscode.pathom.connect.graphql.service-mutations/service}
+                                                       service/addStar                                              #:com.wsscode.pathom.connect{:sym com.wsscode.pathom.connect.graphql.service-mutations/service}
+                                                       service/removeStar                                           #:com.wsscode.pathom.connect{:sym com.wsscode.pathom.connect.graphql.service-mutations/service}
+                                                       service/requestReviews                                       #:com.wsscode.pathom.connect{:sym com.wsscode.pathom.connect.graphql.service-mutations/service}}
+    :com.wsscode.pathom.connect/index-oir             {:service.Customer/cpf               {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/creditCardAccount {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/feed              {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/id                {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/name              {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/preferredName     {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Customer/savingsAccount    {#{:service.Customer/id} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Repository/id              {#{:service.Customer/name
+                                                                                              :service.Repository/name} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service.Repository/name            {#{:service.Customer/name
+                                                                                              :service.Repository/name} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/banks                      {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/creditCardAccount          {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/customer                   {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/repository                 {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/savingsAccount             {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}
+                                                       :service/viewer                     {#{} #{com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}}
+    :com.wsscode.pathom.connect/index-resolvers       #:com.wsscode.pathom.connect.graphql2-test{supposed-resolver {:com.wsscode.pathom.connect.graphql2/graphql? true
+                                                                                                                    :com.wsscode.pathom.connect/cache?            false
+                                                                                                                    :com.wsscode.pathom.connect/sym               com.wsscode.pathom.connect.graphql2-test/supposed-resolver}}})
+
+(deftest test-index-schema
+  (is (= (-> (pcg/index-schema {::pcg/prefix    prefix ::pcg/schema schema
+                                ::pcg/ident-map {"customer"          {"customerId" :service.Customer/id}
+                                                 "creditCardAccount" {"customerId" :service.Customer/id}
+                                                 "savingsAccount"    {"customerId" :service.Customer/id}
+                                                 "repository"        {"owner" :service.Customer/name
+                                                                      "name"  :service.Repository/name}}
+                                ::pcg/resolver  `supposed-resolver})
+             (update-in [::pc/index-resolvers `supposed-resolver] dissoc ::pc/compute-output ::pc/resolve)
+             (update-in [::pc/index-mutations 'com.wsscode.pathom.connect.graphql.service-mutations/service] dissoc ::pc/mutate))
+         indexes)))
+
+(deftest test-alias-for-line
+  (is (= (pcg/alias-for-line "query { \ncustomer(customerId: \"123\") {\n}}" 2)
+         nil))
+
+  (is (= (pcg/alias-for-line "query { \n_customer_customer_id_123: customer(customerId: \"123\") {\n}}" 2)
+         "_customer_customer_id_123"))
+
+  (is (= (pcg/alias-for-line "query { \n_customer_customer_id_123: customer(customerId: \"123\") {\n}}" 10)
+         nil)))
+
+(deftest test-index-graphql-errors
+  (is (= (pcg/index-graphql-errors
+           [{:message   "Parse error on \"-\" (error) at [3 11]"
+             :locations [{:line 3 :column 11}]}])
+         {nil [{:message   "Parse error on \"-\" (error) at [3 11]"
+                :locations [{:line 3 :column 11}]}]}))
+  (is (= (pcg/index-graphql-errors
+           [{:message "Forbidden"
+             :path    ["didWrong"]}])
+         {["didWrong"] [{:message "Forbidden", :path ["didWrong"]}]}))
+  (is (= (pcg/index-graphql-errors
+           [{:message "Forbidden"
+             :path    ["query" "didWrong"]}])
+         {["didWrong"] [{:message "Forbidden", :path ["didWrong"]}]}))
+  (is (= (pcg/index-graphql-errors
+           [{:path       ["mutation" "addStar" "clientMutation"]
+             :extensions {:code      "undefinedField"
+                          :typeName  "AddStarPayload"
+                          :fieldName "clientMutation"}
+             :locations  [{:line 3 :column 5}]
+             :message    "Field 'clientMutation' doesn't exist on type 'AddStarPayload'"}])
+         {["addStar" "clientMutation"] [{:path       ["addStar" "clientMutation"]
+                                         :extensions {:code      "undefinedField"
+                                                      :typeName  "AddStarPayload"
+                                                      :fieldName "clientMutation"}
+                                         :locations  [{:line 3 :column 5}]
+                                         :message    "Field 'clientMutation' doesn't exist on type 'AddStarPayload'"}]})))
+
+(deftest test-parse-item
+  (is (= (pcg/parser-item {::p/entity {}} [])
+         {}))
+  (is (= (pcg/parser-item {::p/entity {:itemValue 42}
+                           ::pcg/demung pg/camel-case}
+           [:ns/item-value])
+         {:ns/item-value 42}))
+  (is (= (pcg/parser-item {::p/entity               {:itemValue {:x 1 :y 2}}
+                           ::p/placeholder-prefixes #{">"}}
+           [{:itemValue [:x {:>/sub [:y]}]}])
+         {:itemValue {:x 1 :>/sub {:y 2}}}))
+  (is (= (pcg/parser-item {::p/entity   {:didWrong nil}
+                           ::pcg/demung pg/camel-case
+                           ::pcg/errors (pcg/index-graphql-errors
+                                          [{:message "Forbidden"
+                                            :path    ["didWrong"]}])}
+           [{:did-wrong [:anything]}])
+         {:did-wrong ::p/reader-error}))
+  (testing "capture error"
+    (let [errors* (atom {})]
+      (is (= (pcg/parser-item {::p/entity          {:_customer_customer_id_123 {:creditCardAccount nil}}
+                               ::p/errors*         errors*
+                               ::pcg/demung        pg/camel-case
+                               ::pcg/base-path     [[:service.Customer/id "123"]]
+                               ::pcg/graphql-query "query \n{_customer_customer_id_123: customer(customerId: \"123\") \n{}}"
+                               ::pcg/errors        (pcg/index-graphql-errors [{:locations [{:column 123 :line 2}]
+                                                                               :message   "Forbidden"
+                                                                               :path      ["customer" "creditCardAccount"]
+                                                                               :type      "forbidden"}])}
+               [{[:customer/customerId "123"] [{:service.Customer/credit-card-account [:service.credit-card-balances/available]}]}])
+             {[:customer/customerId "123"] {:service.Customer/credit-card-account ::p/reader-error}}))
+      (is (= @errors*
+             {[[:service.Customer/id "123"] :service.Customer/credit-card-account] {:locations [{:column 123 :line 2}]
+                                                                                    :message   "Forbidden"
+                                                                                    :path      ["customer" "creditCardAccount"]
+                                                                                    :type      "forbidden"}}))))
+
+  (testing "mutation errors"
+    {:errors
+     [{:path      ["query" "nameWithOwneree"],
+       :extensions
+                  {:code      "undefinedField",
+                   :typeName  "Query",
+                   :fieldName "nameWithOwneree"},
+       :locations [{:line 7, :column 3}],
+       :message
+                  "Field 'nameWithOwneree' doesn't exist on type 'Query'"}]}
+
+    {:errors
+     [{:message   "Parse error on \"-\" (error) at [3 11]"
+       :locations [{:line 3 :column 11}]}]}))
+
+; TODO proper process mutation error responses
+
+(comment
+  (let [errors* (atom {})]
+    [(pcg/parser-item {::p/entity          nil
+                       ::p/errors*         errors*
+                       ::pcg/base-path     []
+                       ::pcg/graphql-query "query {\n  addStar(input: {starrableId: \"MDEwOlJlcG9zaXRvcnk5ODU5MDk2MQ==\"}) {\n    clientMutation\n    starrable {\n      viewerHasStarred\n    }\n  }\n}"
+                       ::pcg/errors        (pcg/index-graphql-errors [{:path       ["mutation" "addStar" "clientMutation"]
+                                                                       :extensions {:code      "undefinedField"
+                                                                                    :typeName  "AddStarPayload"
+                                                                                    :fieldName "clientMutation"}
+                                                                       :locations  [{:line 3 :column 5}]
+                                                                       :message    "Field 'clientMutation' doesn't exist on type 'AddStarPayload'"}])}
+       '[{(:github/addStar
+            {:github/input
+             {:github/starrableId "MDEwOlJlcG9zaXRvcnk5ODU5MDk2MQ=="}})
+          [:clientMutation {:starrable [:viewerHasStarred]}]}])
+     @errors*])
+
+  (let [errors* (atom {})]
+    [(pcg/parser-item {::p/entity          nil
+                       ::p/errors*         errors*
+                       ::pcg/base-path     []
+                       ::pcg/graphql-query "query {\n  addStar(input: {starrableId: \"MDEwOlJlcG9zaXRvcnk5ODU5MDk2MQ==\"}) {\n    clientMutation\n    starrable {\n      viewerHasStarred\n    }\n  }\n}"
+                       ::pcg/errors        (pcg/index-graphql-errors
+                                             [{:message   "Parse error on \"-\" (error) at [3 11]"
+                                               :locations [{:line 3 :column 11}]}])}
+       '[{(:github/addStar
+            {:github/input
+             {:github/starrableId "MDEwOlJlcG9zaXRvcnk5ODU5MDk2MQ=="}})
+          [:clientMutation {:starrable [:viewerHasStarred]}]}])
+     @errors*]))
+
+(comment
+  (println
+    (pcg/query->graphql '[{(:github/addStar
+                             {:github/input
+                              {:github/starrableId "MDEwOlJlcG9zaXRvcnk5ODU5MDk2MQ=="}})
+                           [:clientMutation {:starrable [:viewerHasStarred]}]}]
+      {})))
+
+(deftest test-query->graphql
+  (is (= (pcg/query->graphql [{:credit-card [:number]}] {::pcg/demung pg/camel-case})
+         "query {\n  creditCard {\n    number\n  }\n}\n")))
+
+(defn q [query]
+  (p/query->ast1 [query]))
+
+(deftest test-ast->graphql
+  (is (= (pcg/ast->graphql {:ast         (q :service/banks)
+                            ::pc/indexes indexes} {})
+         [:service/banks]))
+  (is (= (pcg/ast->graphql {:ast         (q {:service/banks [:service.Bank/name]})
+                            ::pc/indexes indexes} {})
+         [{:service/banks [:service.Bank/name]}]))
+  (is (= (pcg/ast->graphql {:ast         (q :service.Customer/cpf)
+                            ::pc/indexes indexes}
+           {:service.Customer/id "123"})
+         [{[:customer/customerId
+            "123"] [:service.Customer/cpf]}])))
+
+(defn query-env [query-attribute entity]
+  {:ast                     (q query-attribute)
+   ::p/entity               entity
+   ::p/placeholder-prefixes #{">"}
+   ::p/parent-query         [query-attribute]
+   ::pcg/prefix             prefix
+   ::pc/indexes             indexes})
+
+(deftest test-build-query
+  (testing "build global attribute"
+    (is (= (pcg/build-query (query-env :service/banks
+                              {:service.Customer/id "123"}))
+           [:service/banks])))
+
+  (testing "remove pathom params"
+    (is (= (pcg/build-query (query-env '(:service/banks {:pathom/as :banks})
+                              {:service.Customer/id "123"}))
+           ['(:service/banks)])))
+
+  (testing "ident join"
+    (is (= (pcg/build-query (query-env :service.Customer/cpf
+                              {:service.Customer/id "123"}))
+           [{[:customer/customerId "123"] [:service.Customer/cpf]}])))
+
+  (testing "ident join on multi param input"
+    (is (= (pcg/build-query (query-env :service.Repository/id
+                              {:service.Customer/name   "customer"
+                               :service.Repository/name "repository"}))
+           [{[:repository/owner-and-name ["customer" "repository"]] [:service.Repository/id]}])))
+
+  (testing "ignores ident queries"
+    (is (= (pcg/build-query (query-env {[:service.Customer/id "123"] [:service.Customer/name]}
+                              {:service.Customer/id "123"}))
+           [])))
+
+  (testing "merge sibling queries"
+    (is (= (pcg/build-query (assoc (query-env :service.Customer/id {:service.Customer/id "123"})
+                              ::p/parent-query [:service.Customer/id
+                                                :service.Customer/cpf
+                                                :service/banks
+                                                :service.Customer/name
+                                                :other/thing]))
+           [{[:customer/customerId "123"] [:service.Customer/cpf :service.Customer/name]}
+            :service/banks])))
+
+  (testing "placeholder queries"
+    (is (= (pcg/build-query (assoc (query-env :service.Customer/id {:service.Customer/id "123"})
+                              ::p/parent-query [:service.Customer/id
+                                                {:>/thing [:service.Customer/cpf]}
+                                                :service/banks
+                                                :service.Customer/name
+                                                :other/thing]))
+           [:service/banks
+            {[:customer/customerId "123"] [:service.Customer/name :service.Customer/cpf]}]))))
+
+(deftest test-pull-idents
+  (is (= (pcg/pull-idents {:service/banks                [{:service.Bank/name "Dino"}]
+                           [:customer/customerId "123"] {:service.Customer/name "Missy"}})
+         {:service/banks         [{:service.Bank/name "Dino"}]
+          :service.Customer/name "Missy"})))
+
diff --git a/test/com/wsscode/pathom/test_helpers.cljc b/test/com/wsscode/pathom/test_helpers.cljc
index 7bd18c20..1d721048 100644
--- a/test/com/wsscode/pathom/test_helpers.cljc
+++ b/test/com/wsscode/pathom/test_helpers.cljc
@@ -2,30 +2,56 @@
 
 (defn mock []
   (let [calls (atom [])]
-    (reify
-      #?(:clj  clojure.lang.IDeref
-         :cljs cljs.core.IDeref)
-      (deref [_] @calls)
+    #?(:clj
+       (reify
+         clojure.lang.IDeref
+         (deref [_] @calls)
 
-      #?(:clj  clojure.lang.IFn
-         :cljs cljs.core.IFn)
-      (invoke [_ v1] (swap! calls conj [v1]))
-      (invoke [_ v1 v2] (swap! calls conj [v1 v2]))
-      (invoke [_ v1 v2 v3] (swap! calls conj [v1 v2 v3]))
-      (invoke [_ v1 v2 v3 v4] (swap! calls conj [v1 v2 v3 v4]))
-      (invoke [_ v1 v2 v3 v4 v5] (swap! calls conj [v1 v2 v3 v4 v5]))
-      (invoke [_ v1 v2 v3 v4 v5 v6] (swap! calls conj [v1 v2 v3 v4 v5 v6]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19]))
-      (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20])))))
+         clojure.lang.IFn
+         (invoke [_ v1] (swap! calls conj [v1]))
+         (invoke [_ v1 v2] (swap! calls conj [v1 v2]))
+         (invoke [_ v1 v2 v3] (swap! calls conj [v1 v2 v3]))
+         (invoke [_ v1 v2 v3 v4] (swap! calls conj [v1 v2 v3 v4]))
+         (invoke [_ v1 v2 v3 v4 v5] (swap! calls conj [v1 v2 v3 v4 v5]))
+         (invoke [_ v1 v2 v3 v4 v5 v6] (swap! calls conj [v1 v2 v3 v4 v5 v6]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19]))
+         (invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20])))
+
+       :cljs
+       (reify
+         IDeref
+         (-deref [_] @calls)
+
+         IFn
+         (-invoke [_ v1] (swap! calls conj [v1]))
+         (-invoke [_ v1 v2] (swap! calls conj [v1 v2]))
+         (-invoke [_ v1 v2 v3] (swap! calls conj [v1 v2 v3]))
+         (-invoke [_ v1 v2 v3 v4] (swap! calls conj [v1 v2 v3 v4]))
+         (-invoke [_ v1 v2 v3 v4 v5] (swap! calls conj [v1 v2 v3 v4 v5]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6] (swap! calls conj [v1 v2 v3 v4 v5 v6]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19]))
+         (-invoke [_ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20] (swap! calls conj [v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20]))))))