beginners

MVitalii 2025-12-18T10:28:04.554819Z

Hi, everyone! I'm building a ClojureScript backend with async API calls. I have a handler that calls an external API and redirects the user. All errors result in the same user-facing error page. Which approach is considered best practice? Option A: Return error maps

;; api.cljs
  (defn create-thing [ctx id]
    (async
     (let [response (await (fetch url opts))]
       (if (oget response :ok)
         (let [data (await (.json response))]
           {:id (:id data) :token (:token data)})
         {:error :api-error
          :status (oget response :status)}))))

  ;; routes.cljs
  (defn handler [ctx req res]
    (async
     (try
       (let [id (oget req [:query :id])
             result (await (create-thing ctx id))]
         (if (:error result)
           (redirect-to-error res)
           (.redirect res (build-url result))))
       (catch js/Error e
         (report-error! e)
         (redirect-to-error res)))))
Option B: Throw exceptions
;; api.cljs
  (defn create-thing [ctx id]
    (async
     (let [response (await (fetch url opts))]
       (when-not (oget response :ok)
         (throw (ex-info "API error" {:status (oget response :status)})))
       (let [data (await (.json response))]
         {:id (:id data) :token (:token data)}))))

  ;; routes.cljs
  (defn handler [ctx req res]
    (async
     (try
       (let [id (oget req [:query :id])
             result (await (create-thing ctx id))]
         (.redirect res (build-url result)))
       (catch js/Error e
         (report-error! e)
         (redirect-to-error res)))))
As a python dev I feel that option B is cleaner since all errors are handled the same way. But from what I understand clojure way is to return data. What's actually the idiomatic approach here?

p-himik 2025-12-18T10:41:33.865489Z

Exceptions are perfectly fine in Clojure. It's just that sometimes they get in the way of other things: • Doing many things and proceeding regardless of failures - you have to wrap every step in try/`catch` and convert exceptions into maps • You can't recur from try or catch because they are not tail positions - there can be a finally clause • If you already have functions that return "error or result" data maps, it might be easier to just stick to those structures instead of going back and forth between data and exceptions When those aren't a concern, I myself stick to exceptions for, well, exceptional situations. Abnormal flows, basically. Definitely don't use exceptions for regular flow control, like a goto.

hrtmt brng 2025-12-20T05:03:39.286419Z

We had the same question a few days ago: https://clojurians.slack.com/archives/C053AK3F9/p1765727779342649

James Amberger 2025-12-18T04:06:20.875909Z

know how you can name an anonymous function (`(fn a-name [] nil)`)? Can something similar be done with reify?

2025-12-18T04:26:40.192989Z

Reify methods all get passed the reified object as the first parameter

James Amberger 2025-12-18T04:39:43.579599Z

I was thinking, if you (def f (fn a-name)) and eval f you get “#function[user/a-name--61550]” but (def r (reify)) -> “#object[user$reify__61554 0x552552d0 “user$reify__61554@552552d0"]” and no way to get a name in there.

phronmophobic 2025-12-18T04:42:49.535919Z

What are you trying to do with the name? You can always override .toString or create a new protocol/interface to override:

> (str (reify
         Object
         (toString [_]
           "what up yo")))
"what up yo"

James Amberger 2025-12-18T04:53:07.199489Z

nothing fancy, in this case I have a donut system and i was checking that a component got started properly so I eval’d it; the start function tries to provide one implementation and falls back to another and if they were both made with reify you can’t tell which one you’ve got just by looking