November 24, 2020

Extending transit in action

Figuring out how to extend transit with cljs-ajax and reitit was a bit of a challenge. So I’m writing it here for future reference, and hopefully others can benefit from it as well.

For this example we will use tick for our extension.

cljs-ajax

(ns example
  (:require [ajax.core :as ajax]
            [ajax.interceptors :as ajax-interceptors]
            [ajax.transit :as ajax-transit]
            [clojure.string :as str]
            [cognitect.transit :as transit]
            [java.time]
            [tick.alpha.api :as t]))

(defn time-fn [obj]
  (str obj))
(defn rep [text]
  (fn [& _]
    text))

(def write-handlers {java.time/Instant (transit/write-handler (rep "tick/instant") time-fn)
                     java.time/Month (transit/write-handler (rep "tick/month") time-fn)
                     java.time/DayOfWeek (transit/write-handler (rep "tick/day-of-week") time-fn)
                     java.time/Year (transit/write-handler (rep "tick/year") time-fn)})
(def writer (transit/writer :json {:handlers write-handlers}))
(def read-handlers {"tick/instant" (transit/read-handler (fn [obj] (t/instant obj)))
                    "tick/month" (transit/read-handler (fn [obj] (t/month obj)))
                    "tick/day-of-week" (transit/read-handler (fn [obj] (t/day-of-week obj)))
                    "tick/year" (transit/read-handler (fn [obj] (t/year obj)))})
(def response-format (ajax-interceptors/map->ResponseFormat
                      {:content-type ["application/transit+json"]
                       :description "Transit response"
                       :read (ajax-transit/transit-read-fn {:handlers read-handlers})}))

(ajax/GET "your-url-here"
  {:writer writer
   :response-format response-format
   :params {:q "somevalue"}
   :handler (fn [response]
              (println response))}

reitit

(ns example
  (:require [cognitect.transit :as transit]
            [muuntaja.core :as m]
            [tick.alpha.api :as t])

(defn time-fn [o]
  (str o))

(def transit-encoder-handlers {java.time.Instant (transit/write-handler "tick/instant" time-fn)
                               java.time.Month (transit/write-handler "tick/month" time-fn)
                               java.time.DayOfWeek (transit/write-handler "tick/day-of-week" time-fn)
                               java.time.Year (transit/write-handler "tick/year" time-fn)})

(def transit-decoder-handlers {"tick/instant" (transit/read-handler (fn [x] (t/instant x)))
                               "tick/month" (transit/read-handler (fn [x] (t/month x)))
                               "tick/day-of-week" (transit/read-handler (fn [x] (t/day-of-week x)))
                               "tick/year" (transit/read-handler (fn [x] (t/year x)))})



;; use muuntaja-instance in the muuntaja ring router instead of the default one
;; used in the examples given in the reitit documenation

(def muuntaja-instance
  (-> m/default-options
      (update-in [:formats] dissoc "application/transit+msgpack" "application/edn")
      (assoc :charsets #{"utf-8"} )
      (update-in [:formats "application/transit+json"] assoc
                 :encoder-opts {:handlers transit-encoder-handlers}
                 :decoder-opts {:handlers transit-decoder-handlers})
      (m/create)))

That’s it. Happy hacking.

Tags: clojure