This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-12
Channels
- # aleph (61)
- # announcements (2)
- # babashka (65)
- # beginners (64)
- # calva (2)
- # clerk (1)
- # cljsrn (1)
- # clojure (60)
- # clojure-austin (7)
- # clojure-europe (13)
- # clojure-italy (2)
- # clojure-losangeles (4)
- # clojure-nl (2)
- # clojure-norway (94)
- # clojure-romania (2)
- # clojure-uk (7)
- # clojuredesign-podcast (5)
- # clojurescript (3)
- # core-typed (2)
- # datomic (42)
- # docker (24)
- # emacs (10)
- # exercism (50)
- # graphql (83)
- # honeysql (25)
- # hyperfiddle (12)
- # malli (13)
- # membrane (49)
- # off-topic (50)
- # podcasts-discuss (1)
- # re-frame (3)
- # reagent (12)
- # reitit (5)
- # releases (2)
- # remote-jobs (8)
What is the preferred way of injecting differentiated context into Query resolvers versus Mutation resolvers versus Subscription resolvers? My first thought was to have a resolver for the root objects (e.g. :Query
) but I can’t decipher the syntax that would allow that (naive attempts prevent the schema from even being compiled). Other options would be to inspect the operations to some extent before feeding the initial context into lacinia (yuck) or wrapping all my resolver fns (yuck). It’s clear from the docs that the root objects have resolvers, but it’s not clear how one would overrride/extend them.
I want to “resolve” all Queries with a single fn before the individual resolvers on the named queries are executed.
What I really want is to modify the context for all Queries separately from the context for all Mutations.
Well, there is a :Query
key in the compiled schema, and it has a :resolver
key on it -and that is an internal lacinia fn.
Right -in interceptor land could work, but what happens when a single request has both a mutation and a query?
make a lacinia context with everything you need (maybe wrapped in a delay), and the root fields pick one?
This seems relevant: https://lacinia.readthedocs.io/en/latest/roots.html#unions
That’s my current thinking, but it is not ideal. One of the goals is to make it hard for queries to use the wrong db value and for mutations to read outside the scope of a transaction (all this in Datomic, BTW).
For queries, I want to remove the connection from the context and only have a db value. For mutations I want to remove the db value and only provide a connection.
you can do this pretty easily with some helper using`l.resolve/with-context` and adjusting what’s available before calling a descendent resolver but you have to “wrap” every root field resolver.
there’s no field “edge” going into the root objects, so I’m not surprised if this isn’t possible
It seems like the place to hang the resolver is on the :Query
object. In fact, there is already a resolver there (`wrap-resolver-to-ensure-resolver-result`)
objects don’t have resolvers, so I don’t see how wrap-resolver-to-ensure-resolver-result
is relevant
Weird,,, I can see all the keys on the compiled schema. And they include those three objects.
(-> (l.schema/compile {})
:Query
keys)
=> (:category :type-name :description :fields :directives :compiled-directives :implements :tag :compiled-schema)
root-value (_::resolved-value_ context)
(line 388 in my version) is where it’s initialized
so you could set ::executor/root-value
on your context in your interceptor, but that still doesn’t let you any closer to what you want vs using public interfaces
An operation is a (possibly named and parameterized) selection of fields from one of the root objects
OK. So it’s not possible to have multiple operations in the same request -that simplifies the problem quite a bit.
Right, but in terms of differentiated context, that means only one setup of context per “request”
> To execute a request, the executor must have a parsed https://spec.graphql.org/draft/#Document and a selected operation name to run if the document defines multiple operations, otherwise the document is expected to only contain a single operation. The result of the request is determined by the result of executing this operation according to the “Executing Operations” section below.
This is gql minutia, I don’t think you’ll encounter this in the wild outside of extensions
I know the apollo ecosystem has some extension that allows operation batching, but it’s basically http pipelining
I assumed that because the syntax of a Document can include multiple operations that it was possible execute multiple operations in a single request. Wrong!
circling back, there’s probably some way in interceptor-land to inspect the root context right before it is sent to execute-query, determine if it’s a mutation/query/subscription, alter the app-context appropriately, and then send it down
But I’m still going to have to hack something together to differentiate the contexts. Although now it’s feasible to do it once in an interceptor.
honestly wrapping isn’t a bad idea in general because of how error handling works with promises. a catch-all-exceptions and with-warning/error if caught is a lifesaver
I’ll take a look at the parsed (but not executed) query and see if the operation type is easily discerned. If not, I’m going to wrappers.