How can I make an http call in clojure/ring?

1.5k views Asked by At

My web client (written in cljs) connects to backend (written in clj) which needs to make a few third party API calls. It has to be done on the server and then the result should be transformed in a specific way and sent back to the client.

Here's my handler for one of the urls

(defn get-orders [req]
  (let [{:keys [sig uri]} (api-signature :get-orders)]
    (client/get uri
                {:async? true}
                (fn [response] {:body "something"})
                (fn [exception] {:body "error"}))))

Instead of returning {:body "something"}, it is returning the following error:

No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: org.apache.http.impl.nio.client.FutureWrapper

What am I doing wrong?

1

There are 1 answers

1
tap On BEST ANSWER

When you specify {:async? true}, clj-http.client/get will return a future which is a FutureWrapper in the error message you got.

So if you don't need async, don't use it. This is an example of a synchronous ring handler which calls a third-party url and returns the response that got back.

(defn handler [request]
  (response {:result (client/get "http://example.com")}))

If you really need async, use async version of ring handler.

(defn handler [request respond raise]
  (client/get "http://example.com"
              {:async? true}
              (fn [response] (respond {:body "something"}))
              (fn [exception] (raise {:body "error"}))))

Don't forget to config webserver adapter to use the async handler. For example, for Jetty, set :async? flag to true like so

(jetty/run-jetty app {:port 4000 :async? true :join? false})

If you want to concurrently call to multiple third-party urls and return once to web client, use promise to help

(defn handler [request]
  (let [result1 (promise)
        result2 (promise)]
    (client/get "http://example.com/"
                {:async? true}
                (fn [response] (deliver result1 {:success true :body "something"}))
                (fn [exception] (deliver result1 {:success false :body "error"})))
    (client/get "http://example.com/"
                {:async? true}
                (fn [response] (deliver result2 {:success true :body "something"}))
                (fn [exception] (deliver result2 {:success false :body "error"})))
    (cond
      (and (:success @result1)
           (:success @result2))
      (response {:result1 (:body @result1)
                 :result2 (:body @result2)})

      (not (:success @result1))
      (throw (ex-info "fail1" (:body @result1)))

      (not (:success @result2))
      (throw (ex-info "fail2" (:body @result2))))))