clojure

Ring server returning empty response for Persistent array maps, but works for lazySeq. How to return Persistent array maps also?


I'm learning to write a simple rest api in clojure that simply serves some data through REST API to serve as middleware for a mysql db.

I followed some tutorials and setup my ring server to convert clojure data to json. My project has these dependencies:

             ; middleware
             [compojure "1.6.2"]
             [ring/ring-defaults "0.3.2"]
             [ring/ring-json "0.5.1"]
             [ring-cors "0.1.13"]
             ; API
             [clj-http "3.12.3"]
             [cheshire "5.11.0"]
             ; MYSQL
             [org.clojure/clojure-contrib "1.2.0"]
             [org.clojure/java.jdbc "0.7.12"]
             [mysql/mysql-connector-java "8.0.33"]]

And my main handler file has something like this:

(defroutes app-routes 
  (GET "/get-data" [type] (api/get-users type)))

(def app
  (-> app-routes
  (wrap-cors :access-control-allow-origin [#".*"]
             :access-control-allow-methods [:get :put :post :delete])
  (middleware/wrap-json-body {:key-fn keyword})
  (middleware/wrap-json-response)))

Everything works okay as long as the return data is a lazy sequence. I can curl the url and I get valid json array. But when the response is just a json object without an array, I get empty response. I tried printing the data and the right data is being fetched from database just fine. Then I tried printing the data types for each type of response and I have different types.

clojure.lang.LazySeq -> works and I get valid json response. But clojure.lang.PersistentArrayMap -> doesn't work and I get an empty response.

When I print the data just before I return it to the browser, I get these:

For lazySeq:

({:id spanish-chorizo-paella, :name Spanish chicken & chorizo paella})
// ^ this works fine and I get a JSON array in response

For PersistentArrayMap:

{:latest 202410-hu, :past [{:id 202212-nl-ch-fr}]}
// ^ This doesn't work and I get an empty response in browser

Is there something I need to do to get the ring server to detect and convert the second type to correctly send as json?


Solution

  • The Ring spec is for a handler to return a response map, which contains :status, :headers. and optionally :body keys. You are attempting to return some other map, whatever the API gives you, and hoping it will be turned into JSON. Since your handler isn't following the Ring spec, the behavior of middlewares wrapped around it is undefined: apparently they happen to return something sensible when you return a lazy sequence, but this is not guaranteed, and doesn't happen when you return a map.

    Instead of relying on this, update your handler to return a response map, with the JSON body you want to encode in the :body. You could do this by hand, by returning {:status 200 :body (api/get-users type)}, but it's typical to delegate to ring.util.response/response, which does this for you.