Kvlt
Multi-target Clojure/script HTTP client
Install / Use
/learn @nervous-systems/KvltREADME
Attempts to present a uniform, asychronous client interface for HTTP across JVM / Node / browsers.
Latest documentation / examples
Features
- Supports Clojure/JVM, Clojurescript/Node and Clojurescript/Browser
- Individual deferred values are exposed via promises (kvlt.core), or asynchronous channels (kvlt.chan)
core.async-based support for Websockets and Server-sent Events- Raw responses available as Javascript typed arrays (on Node, and in browsers with XHR Level 2 support)
- Ring-like API
Requirements
- Clojure use requires JDK8
Todo / Notes
- Automated/CI testing is currently limited to JVM, Node and recent Chrome & Firefox builds
- No support for streamed requests/responses. Open to suggestions about how this might be handled across platforms
- Young project, etc. - please file issues
Examples
kvlt.core/request!
returns a promesa promise, which
can be manipulated using promise-specific (e.g. promesa/then)
operations, or treated as a monad using the primitives from
cats. Below, we're working with
something like:
(ns kvlt.examples
(:require [kvlt.core :as kvlt]
[promesa.core :as p]))
The default :method is :get:
(p/alet [{:keys [status]} (p/await (kvlt/request! {:url url}))]
(is (= status 200)))
Explicit Callback
(p/then
(kvlt/request! {:url url})
(fn [{:keys [status]}]
(is (= status 200))))
core.async
The kvlt.chan namespace
parallels the promise-driven kvlt.core, using asynchronous channels
to communicate deferred values.
(go
(let [{:keys [status]} (<! (kvlt.chan/request! {:url url}))]
(is (= status 200))))
Writing Data
In addition to Ring-style :form-params/:type, metadata may be
applied to :body, indicating the desired content-type and body
serialization:
(p/alet [{:keys [body]}
(p/await
(kvlt/request!
{:url url
:method :post
:body ^:kvlt.body/edn {:hello "world"}
:as :auto}))]
(is (= (body :hello) "world")))
:as :auto causes the :body key in the response to be processed in
accord with the response's content-type. :as :edn, in this case,
would have the same effect.
Errors
(p/catch
(kvlt/request! {:url (str url "/404")})
(fn [e]
(is (= :not-found ((ex-data e) :type)))))
All requests resulting in exceptional response codes, or more
fundamental (e.g. transport) errors will cause the returned promise's
error branch to be followed with an
ExceptionInfo
instance - i.e. an Exception/Error with an associated response map
retrievable via ex-data.
Example Map
{:headers {:content-type "text/plain" ...}
:type :not-found
:reason :not-found
:status 404
...}
More request/response examples
Server-sent events
(def events (kvlt.core/event-source! url))
events is a core.async channel (rather, something a lot like a
core.async channel, which'll terminate the SSE connection when
async/close!'d).
Assuming no event types or identifiers are supplied by the server, a
value on events looks something like:
{:type :message
:data "String\nfrom\nthe server"}
Websockets
kvlt.core/websocket!
takes a URL, and returns a promise which'll resolve to a core.async
channel:
(p/alet [ws (p/await (kvlt/websocket! "http://localhost:5000/ws" {:format :edn}))]
(go
(>! ws {:climate :good, :bribery :tolerated})
(let [instructions (<! ws)]
(is (instructions :proceed?)))))
Closing the ws channel will terminate the websocket connection.
License
kvlt is free and unencumbered public domain software. For more information, see http://unlicense.org/ or the accompanying UNLICENSE file.
Related Skills
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.6kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.6kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
