{"id":13760482,"url":"https://github.com/jarohen/chord","last_synced_at":"2025-12-12T01:24:09.138Z","repository":{"id":9865548,"uuid":"11863945","full_name":"jarohen/chord","owner":"jarohen","description":"A library designed to bridge the gap between the triad of CLJ/CLJS, web-sockets and core.async.","archived":false,"fork":false,"pushed_at":"2020-07-12T09:57:33.000Z","size":151,"stargazers_count":439,"open_issues_count":7,"forks_count":40,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-10-30T01:59:52.108Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jarohen.png","metadata":{"files":{"readme":"README.org","changelog":"CHANGES.org","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-03T14:04:48.000Z","updated_at":"2024-08-31T21:35:58.000Z","dependencies_parsed_at":"2022-09-19T03:31:17.772Z","dependency_job_id":null,"html_url":"https://github.com/jarohen/chord","commit_stats":null,"previous_names":["james-henderson/chord"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarohen%2Fchord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarohen%2Fchord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarohen%2Fchord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarohen%2Fchord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jarohen","download_url":"https://codeload.github.com/jarohen/chord/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378149,"owners_count":20929297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T13:01:11.180Z","updated_at":"2025-12-12T01:24:09.090Z","avatar_url":"https://github.com/jarohen.png","language":"Clojure","funding_links":[],"categories":["Clojure","Tools per Language","WebSocket","Awesome ClojureScript"],"sub_categories":["Java VM","WebSockets"],"readme":"* Chord\n\nA lightweight Clojure/ClojureScript library designed to bridge the gap\nbetween the triad of CLJ/CLJS, web-sockets and core.async.\n\n** Usage\n\nInclude the following in your =project.clj=:\n\n#+BEGIN_SRC clojure\n  [jarohen/chord \"0.8.1\"]\n#+END_SRC\n\nChord now supports EDN, JSON, Transit and Fressian out of the box -\nplease remove dependencies to 'chord-fressian' and 'chord-transit' if\nyou have them. Thanks to [[https://github.com/lsnape][Luke Snape]], [[https://github.com/rosejn][Jeff Rose]] and [[https://github.com/tgetgood][Thomas Getgood]] for\ntheir help supporting these formats!\n\n*** Example project\n\nThere is a simple example server/client project under the\n=example-project= directory. The client sends websocket messages to\nthe server, that get echoed back to the client and written on the\npage.\n\nYou can run it with =lein dev= - an alias that starts up an http-kit\nserver using [[https://github.com/james-henderson/lein-frodo][Frodo]] and automatically re-compiles the CLJS.\n\nOnce it is running - navigate to [[http://localhost:3000/]] and you should see =Send a message to the server:=\n\n*** ClojureScript\n\n*Chord* only has one function, =chord.client/ws-ch=, which takes a\nweb-socket URL and returns a map, containing either =:ws-channel= or\n=:error=. When the connection opens successfully, this channel then\nreturns a two-way channel that you can use to communicate with the\nweb-socket server:\n\n#+BEGIN_SRC clojure\n  (:require [chord.client :refer [ws-ch]]\n            [cljs.core.async :refer [\u003c! \u003e! put! close!]])\n  (:require-macros [cljs.core.async.macros :refer [go]])\n\n  (go\n    (let [{:keys [ws-channel error]} (\u003c! (ws-ch \"ws://localhost:3000/ws\"))]\n      (if-not error\n        (\u003e! ws-channel \"Hello server from client!\")\n        (js/console.log \"Error:\" (pr-str error)))))\n#+END_SRC\n\nMessages that come from the server are received as a map with a\n=:message= key:\n\n#+BEGIN_SRC clojure\n  (go\n    (let [{:keys [ws-channel]} (\u003c! (ws-ch \"ws://localhost:3000/ws\"))\n          {:keys [message]} (\u003c! ws-channel)]\n      (js/console.log \"Got message from server:\" (pr-str message))))\n#+END_SRC\n\nErrors in the web-socket channel (i.e. if the server goes away) are\nreturned as a map with an =:error= key:\n\n#+BEGIN_SRC clojure\n  (go\n    (let [{:keys [ws-channel]} (\u003c! (ws-ch \"ws://localhost:3000/ws\"))\n          {:keys [message error]} (\u003c! ws-channel)]\n      (if error\n        (js/console.log \"Uh oh:\" error)\n        (js/console.log \"Hooray! Message:\" (pr-str message)))))\n#+END_SRC\n\nAs of 0.3.0, you can pass a =:format= option, to pass messages over\nthe channel as EDN (default), as raw strings, or JSON (0.3.1). Valid\nformats are =#{:edn :json :json-kw :str :fressian :transit-json}=,\ndefaulting to =:edn=.\n\n(If you do use fressian, you'll need to require =chord.format.fressian=,\nin addition to the usual Chord namespaces)\n\n#+BEGIN_SRC clojure\n  (:require [cljs.core.async :as a])\n  (ws-ch \"ws://localhost:3000/ws\"\n         {:format :json-kw})\n#+END_SRC\n\nAs of 0.2.1, you can configure the buffering of the channel by\n(optionally) passing custom read/write channels, as follows:\n\n#+BEGIN_SRC clojure\n  (:require [cljs.core.async :as a])\n  (ws-ch \"ws://localhost:3000/ws\"\n         {:read-ch (a/chan (a/sliding-buffer 10))\n          :write-ch (a/chan 5)})\n#+END_SRC\n\nBy default, Chord uses unbuffered channels, like core.async itself.\n\n*** Clojure\n\n*Chord* wraps the websocket support provided by [[http://http-kit.github.io][http-kit]], a fast\nClojure web server compatible with Ring.\n\n*N.B. Currently, Ring's standard Jetty adapter ~does not~ support\nWebsockets.*  [[http://http-kit.github.io][http-kit]] is a Ring-compatible alternative.\n\nAgain, there's only one entry point to remember here: a wrapper around\nhttp-kit's =with-channel= macro. The only difference is that, rather\nthan using http-kit's functions to interface with the channel, you can\nuse core.async's primitives.\n\nChord's =with-channel= is used as follows:\n\n#+BEGIN_SRC clojure\n  (:require [chord.http-kit :refer [with-channel]]\n            [org.httpkit.server :refer [run-server]]\n            [clojure.core.async :refer [\u003c! \u003e! put! close! go]])\n\n  (defn your-handler [req]\n    (with-channel req ws-ch\n      (go\n        (let [{:keys [message]} (\u003c! ws-ch)]\n          (prn \"Message received:\" message)\n          (\u003e! ws-ch \"Hello client from server!\")\n          (close! ws-ch)))))\n#+END_SRC\n\nThis can take a =:format= option, and custom buffered read/write\nchannels as well:\n\n#+BEGIN_SRC clojure\n  (require '[clojure.core.async :as a])\n\n  (defn your-handler [req]\n    (with-channel req ws-ch\n      {:read-ch (a/chan (a/dropping-buffer 10))\n           :format :str} ; again, :edn is default\n      (go\n        (let [{:keys [message]} (\u003c! ws-ch)]\n          (prn \"Message received:\" message)\n          (\u003e! ws-ch \"Hello client from server!\")\n          (close! ws-ch)))))\n#+END_SRC\n\nYou can also use the =wrap-websocket-handler= middleware, which will\nput a =:ws-channel= key in the request map:\n\n#+BEGIN_SRC clojure\n  (require '[chord.http-kit :refer [wrap-websocket-handler]]\n           '[org.httpkit.server :refer [run-server]]\n           '[clojure.core.async :as a])\n\n  (defn your-handler [{:keys [ws-channel] :as req}]\n    (go\n      (let [{:keys [message]} (\u003c! ws-channel)]\n        (println \"Message received:\" message)\n        (\u003e! ws-channel \"Hello client from server!\")\n        (close! ws-channel))))\n\n  (run-server (-\u003e #'your-handler wrap-websocket-handler) {:port 3000})\n#+END_SRC\n\nYou can pass custom channels to =wrap-websocket-handler= as a second\n(optional) parameter:\n\n#+BEGIN_SRC clojure\n  (run-server (-\u003e #'your-handler\n                (wrap-websocket-handler {:read-ch ...}))\n              {:port 3000})\n#+END_SRC\n\n** Bug reports/pull requests/comments/suggestions etc?\n\nYes please! Please submit these in the traditional GitHub manner.\n\n** Contributors\n\nChord's contributors are listed in the ChangeLog - thank you all for\nyour help!\n\n** License\n\nCopyright © 2013-2015 James Henderson\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarohen%2Fchord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarohen%2Fchord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarohen%2Fchord/lists"}