Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

java-http-clj CircleCI cljdoc badge

clj-http is the de-facto standard HTTP client for Clojure. It is an excellent library, but it is also a large dependency since it is based on Apache HTTP. It also doesn't support HTTP/2 (yet).

Enter java-http-clj. It is inspired by both clj-http and Ring and built on that ships with with Java 11. As such it comes with no extra dependencies if you're already using Java 11 and it fully supports HTTP/2 out of the box.


Current Version

java-http-clj requires Clojure 1.9+ and Java 11+.



First, require the library:

(:require [java-http-clj.core :as http])

The most common HTTP methods (GET, POST, PUT, HEAD, DELETE) have a function of the same name. This function takes three arguments (where the last two are optional): a URL, a request and an options map (refer to send docs for details).

  • GET requests
;; If you don't specify any options defaults are provided
(http/get "")

;; With request options
(http/get "" {:headers {"Accept" "application/json"
                                             "Accept-Encoding" ["gzip" "deflate"]}
                                   :timeout 2000})
  • POST/PUT requests
(http/post "" {:body "{\"foo\":\"bar\"}"})

;; The request body can be a string, an input stream or a byte array...
(http/post "" {:body (.getBytes "{\"foo\":\"bar\"}")})

;; ...and you can choose the response body format with the `:as` option
(http/post "" {:body "{\"foo\":\"bar\"}"} {:as :byte-array})
  • Async requests

To make an async request, use the send-async function (currently there is no sugar for async requests):

;; Returns a java.util.concurrent.CompletableFuture
(http/send-async {:uri "" :method :get})

;; Takes an optional callback and exception handler
(http/send-async {:uri "" :method :get}
                 (fn [r] (do-something-with-response r))
                 (fn [e] (println "oops, something blew up")))
  • Options

All request functions take an opts map for customization (refer to send docs for details).

;; Provide a custom client
(def client (http/build-client {:follow-redirects :always}))
(http/send {:uri "" :method :get} {:client client})

;; Skip map conversion and return the object
user=> (http/send {:uri "" :method :get} {:raw? true})
object[ "0x88edd90" "(GET 200"]


java-http-clj also includes a WebSocket API. The WebSocket API in is based around CompletableFuture and functional interfaces which interop poorly with Clojure. Hence, java-http-clj presents a simplified, synchronous API that covers the basic use-cases.

The API consists of three methods: build-websocket, send and close. The Java API requires you to maintain a request counter for each invocation, but java-http-clj manages this for you automatically.

;; Create a websocket
(def ws
    {:on-text (fn [ws string last?]
                (println "Received some text!" string))
     :on-binary (fn [ws byte-array last?]
                  (println "Got some bytes!" (vec byte-array)))
     :on-error (fn [ws throwable]
                 (println "Uh oh!" (.getMessage throwable)))}))

;; Send some data (strings, ByteBuffers, byte arrays or something that can be coerced to a string)
   and return the websocket
(-> ws
    (send "abc")
    (send (byte-array [1 2 3]))
    (send 123))

;; Close the output of websocket when you are done
(close ws)

There is also build-websocket-async and send-async that return a CompletableFuture. These functions are probably best used together with some asynchronous framework that allows you to compose CompletableFutures (for example Manifold).



  • Lightweight: zero dependencies and a small codebase
  • Performant: minimal overhead compared to direct interop
  • Flexible: make everyday use-cases easy and advanced use-cases possible


  • Hide away the Java
    • Common use-cases should not require interop, but more advanced uses might require dipping in to the underlying Java API.
  • Completeness
    • Not everything will be provided through Clojure. For example, most of the classes in HttpResponse.BodySubscribers are not available directly through Clojure, since Clojure has its own stream-processing facilities that are more idiomatic than the Java equivalents.
  • Input/output conversion
    • java-http-clj does not have any built-in facilities for formats such as JSON, Transit or YAML. It provides the choice of "raw data" through the :as option but leaves it up to the user to parse the data as they see fit

If any of this is deal-breaker for you, I recommend clj-http which is a much more fully-featured HTTP client.


Copyright © 2018-2021 John Schmidt

Released under the MIT License:


Clojure wrapper for with async, HTTP/2 and WebSockets







No packages published

Contributors 4