This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-11-27
Channels
- # adventofcode (1)
- # announcements (4)
- # beginners (120)
- # calva (5)
- # cider (12)
- # clara (3)
- # cljdoc (48)
- # cljs-dev (33)
- # cljsrn (4)
- # clojure (124)
- # clojure-dev (43)
- # clojure-europe (2)
- # clojure-italy (168)
- # clojure-nl (2)
- # clojure-spec (7)
- # clojure-uk (79)
- # clojurescript (50)
- # core-logic (6)
- # cursive (12)
- # datascript (1)
- # datomic (8)
- # devcards (2)
- # emacs (5)
- # events (2)
- # figwheel-main (6)
- # fulcro (18)
- # graphql (42)
- # hyperfiddle (3)
- # jobs (1)
- # luminus (2)
- # nrepl (5)
- # off-topic (59)
- # onyx (5)
- # parinfer (2)
- # pathom (10)
- # pedestal (2)
- # portkey (3)
- # re-frame (24)
- # reagent (6)
- # reitit (54)
- # remote-jobs (1)
- # ring (5)
- # shadow-cljs (75)
- # spacemacs (35)
- # sql (22)
- # tools-deps (16)
- # unrepl (10)
Hi! How do you deal with GraphQL data you receive, which has circular structures (-> app :img :app :img ...)
, when both the "application" and the "image" contain fields of the same name (e.g. :id
)? How do you spec that? I tried to put the two into different namespaces to be able to distinguish the keywords by namespace, but that will create circular dependencies...
And how do you spec the incomplete data that you fetch? Especially when you try to share spec definitions across services? I.e. if I fetch an "application" from a service, there it contains "instances running", which are speced, but not all of my clients might require and this data and hence not fetch it, so the original spec will not validate this partial data...
I’m not sure I understand 100% what you’re asking, but one thing to note is that you don’t create circular dependencies if you’re just using namespaced keywords. E.g. using :app/id
is quite possible without ever require
-ing the app
namespace.
@U7PBP4UVA So if I have circular structures in my data, I would be forced to use simple :keywords
as opposed to ::namespaced/keywords
for the keys? Thought maybe I could (def)
something or so, like I would do in C, when I get into header #include
trouble.
You can use :fully.qualified.namespace/keyword
with a single colon and the full namespace. You cannot use ::namespace-alias/keyword
with two colons without requiring the namespace (or, there’s no easy way, I think there are some hacks).
I should add that I tried to create something like (ns application) (s/def ::id string?)
, i.e. ideally the different fields would have keywords bound to a namespace, so that I can distinguish them.
So ::alias/kw
is exactly equivalent to :fqns/kw
? I always assumed that ::
created an object that cannot be created using :
.
Sorry, missed this — yes, ::alias/kw
expects to find a (:require [some.ns :as alias])
and then is 100% equivalent to some.ns/kw
.
@U7PBP4UVA So you generally have different specs on the consumer than on the producer, because the consumer might opt to request only parts of the data that the producer deems to be required to form a complete object?
In general I think GraphQL and spec have a little bit of friction, but can easily play well together. I don’t have a huge experience myself, so it’d help if you post some example code so that we can talk specifics.
There will be a talk at the conj in a few days that deals with spec and GraphQL and I’m looking forward to it.
"... two specs": And because they will have to use different keywords, I will rewrite the keys of my maps when I receive them through GraphQL, but before I validate and store them?
One key idea of spec, I believe, is to be able to separate the “keys” of a map, e.g. :user/email
has to be a string, of this length, valid email etc etc, to the “aggregation” of those keys (e.g. a user has to have at least a :user/id
and :user/email
.
re-frame (it uses an internal db that is available to event handlers registered with re-frame)
(require '[clojure.spec.alpha :as s])
(defn email? [x]
true)
(s/def :user/id string?)
(s/def :user/email (s/and string? email?))
(s/def :user/user-creation (s/keys :req-un [:user/id :user/email]))
(s/valid? :user/user-creation {:id "abc" :email ""})
(s/def :user/user-fetch (s/keys :opt-un [:user/id :user/email]))
(s/valid? :user/user-fetch {:email ""})
Note how :user/id
and :user/email
are shared between :user/user-creation
, :user/user-fetch
, but in one they are required, whereas in the other optional.
What about data structures like these: {:users [{:id ... :email ...}]}
I thought I need to (s/def :users (s/* :user))
and (s/def :user (s/keys :req-un [:id :email]))
and the :user
keyword in both have to match, in order for spec to understand that one refers to the other.
And given these it becomes harder to have to definitions of :users
(plural) where one contains :user
s (singular) where :email
is required and the other does not require this keyword. (e.g.)
I would think that now I have to recreate the whole data structure / tree. And what's required (and hence fetched with GraphQL) and what not depends a lot on where in the application the data is used...
So you mean that depending on the query, sometimes you will need to send back: {:users [{:email ...} {:email ...}]}
and others: {:users [{:name ...} {:name ...}]}
If this is the case, I’d have my :user
contain only optional keys, then have another pass using a simple function to validate that every element of that vector contains only the expected keys.
(Note: I think spec best practice, if not enforced, is to use namespaced keywords when doing (s/def ...)
Re: Note: This is exactly how I arrived at the fqns issue mentioned in the other thread.
Re: "that vector contains only the expected keys": So I have a loose spec on the consumer side and then use another function to validate that the subset of data required for this operation is actually present? And if it is present, I can be sure that it follows the spec. I just have to validate it down to the leaves, in order to ensure it is complete?
Yes, more or less. I would expect the graphql library to do some sort of validation as well though.
Note as well — there is work underway for a programmatic API to spec, so that you are able to create specs on the fly. But its ETA is unknown at the moment.
I wrote down what I think I need in #clojure-spec: https://clojurians.slack.com/archives/C1B1BB2Q3/p1543332350479400, because that channel seems more suitable to this question in retrospect.