I’d like to write a Kit web application (REST API) that handles JSON in POST endpoints. (EDIT: It is available in this Git repo: GitHub - patrickbucher/kit-example)
I created a new Kit application:
clj -Tclj-new create :template io.github.kit-clj :name com.github.patrickbucher/kit-example
In src/clj/com/github/patrickbucher/kit_example/web/controllers/health.clj
, I added another handler function:
(defn echo [req]
(:body req))
Which I route in src/clj/com/github/patrickbucher/kit_example/web/routes/api.clj
:
(defn api-routes [_opts]
[["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "com.github.patrickbucher.kit-example API"}}
:handler (swagger/create-swagger-handler)}}]
["/health" {:get #'health/healthcheck!}]
["/echo" #'health/echo]]) ;; <<-- this is my code
Now I start the application:
$ clj -M:dev:nrepl
user=> (go)
The healthcheck works:
curl -X GET localhost:3000/api/health
Result:
{"time":"Mon Apr 21 08:13:49 CEST 2025","up-since":"Mon Apr 21 08:13:10 CEST 2025","app":{"status":"up","message":""}
But my echo
endpoint fails:
curl -X POST localhost:3000/api/echo -H 'Accept: application/json' -H 'Content-Type: application/json' -d '{"foo": "bar"}'
Error Message:
2025-04-21 08:15:10,902 [XNIO-2 task-2] ERROR io.undertow.request - UT005071: Undertow request failed HttpServerExchange{ POST /api/echo}
java.lang.IllegalArgumentException: contains? not supported on type: io.undertow.io.UndertowInputStream
So I’m dealing with an UntertowInputStream
, not with a map based on the deserialized JSON input.
I alter the echo
endpoint as follows for debugging:
(defn echo [req]
(println (slurp (:body req)))
{:foo "bar"})
After which I get the following exception:
2025-04-21 08:20:05,022 [XNIO-3 task-2] ERROR c.g.p.k.web.middleware.exception - #error {
:cause UT000034: Stream is closed
:via
[{:type java.io.IOException
:message UT000034: Stream is closed
:at [io.undertow.io.UndertowInputStream read UndertowInputStream.java 110]}
Since there must be a conceptual misunderstanding, I spare you the whole stack trace.
In src/clj/com/github/patrickbucher/kit_example/web/middleware/formats.clj
, the following is defined (I didn’t change it):
(ns com.github.patrickbucher.kit-example.web.middleware.formats
(:require
[luminus-transit.time :as time]
[muuntaja.core :as m]))
(def instance
(m/create
(-> m/default-options
(update-in
[:formats "application/transit+json" :decoder-opts]
(partial merge time/time-deserialization-handlers))
(update-in
[:formats "application/transit+json" :encoder-opts]
(partial merge time/time-serialization-handlers)))))
How can I request the request body as a Clojure data structure?