Basic transit-clj + transit-cljs example?

I’m trying out the clj and cljs transit libraries in a Clojurescript and Ring/Compojure demo, and while it kind of works I’m confused by something that I’m hoping someone can shed light on.

When I read about transit it seems like a basic use case is for transit-clj to encode Clojure forms on the server, which you can then send as application/transit+json to the client to be read by transit-cljs. The encoding server-side seems to work OK, but transit-cljs never reads the response – it fails with an error that you would get if you’re not reading valid JSON (“unexpected character at line 1 column 2 of the JSON”), or at least that’s what my searching tells me).

To read the data I just don’t use transit-cljs and treat the response as regular JSON, which oddly works OK.

This is the clj code on the server:

(def db (atom []))

(defn transit-out [data]
  (let [out (ByteArrayOutputStream. 4096)
        writer (transit/writer out :json)]
    (transit/write writer data)
    (.toString out)))

(defroutes app-routes
;....
  (GET "/guestbook" []
       (-> (transit-out @db)
            (response)
            (content-type "application/transit+json")))
  (route/not-found "Not found."))

And the cljs client-side:

(defn get-guestbook []
  (go
    (let [data  (<p! (.json (<p! (js/fetch "guestbook"))))
          guestbook (.querySelector js/document "#entries")]
      (set! (.-innerHTML guestbook) (clojure.string/join "<br>" data)))))

I thought I would have to use transit-cljs here to read the response of the js/fetch, but code like:

(let [data (transit/read (transit/reader :json) (<p! (js/fetch "guestbook")))] ....

fails with that error about ‘unexpected character’. So I’ve been just reading it as regular JSON instead.

Am I missing something basic to allow transit-cljs to read the response as transit+json?

transit/read in CLJS expects a string as the second argument. You are feeding it a fetch response, which it won’t understand. Change (js/fetch "guestbook") to (-> (js/fetch "guestbook) (.then #(.text %))) and it should work.

Thanks, I have a feeling I’m getting confused in a maze of Promises again. This code has the same issue,


[data (transit/read (transit/reader :json) (-> (js/fetch "guestbook") (.then #(.text %))))]

However this works,

[data  (transit/read (transit/reader :json) (-> (js/fetch "guestbook") (<p!) (.text) (<p!)))]

I wonder if it has something to do with all this happening in a go block? Pulling it out into a standalone function:

(defn ^:export myPost []
  (let [data (transit/read (transit/reader :json) (-> (js/fetch "guestbook") (.then #(.text %))))
        guestbook (.querySelector js/document "#entries")]
    (set! (.-innerHTML guestbook) (clojure.string/join "<br>" data))))

returns the same error (“Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data”).

Copying the raw json from dev tools it looks like

XHRGEThttp://localhost:3000/guestbook
[HTTP/1.1 200 OK 1ms]

1

["hi","hi","hi","","hi","hi","hi","hi","hi","","","","","","","","","","","","","","","","","","","",""]

For either the response with the parsing error, or the successful read by transit-cljs.

I tried a few different ways of promise chaining without the <p! macro but couldn’t get it to work…must be doable though if the macro works.

Just don’t use go or <p! would be my suggestion.

Its pretty straightforward with plain promise chaining.

(defn get-guestbook []
  (-> (js/fetch "guestbook")
      (.then #(.text %))
      (.then (fn [text]
               (let [data (transit/read (transit/reader :json) text)
                     guestbook (js/document.querySelector "#entries")]
                 
                 (set! (.-innerHTML guestbook) (clojure.string/join "<br>" data))
                 )))
      (.catch (fn [err]
                (js/console.log "something failed" err)))))

1 Like