pedestal

2026-05-17T22:37:19.569269Z

Are there any up to date examples on how to setup pedestal from the template app (http-kit) and websockets? The example in the repo is from 3years ago…

souenzzo 2026-05-18T13:12:23.615859Z

I'm studying this and should contribute soon. Given your router

...
(conn/with-routes
                 #{["/" :get index-handler]
                     ...})
Add a "websocket" route
#{["/" :get index-handler]
    ["/ws" :get (websocket/websocket-interceptor ::ws ws-map)]
    ....}
Where your ws-map should look like this:
(def ws-map
   {:on-open (fn [channel ring-request] (websocket/start-ws-connection channel nil))
   :on-text (fn [channel proc text] ...)
    :on-close (fn [channel proc reason] ...)})
Where: • channel is "the internal websocket" object, • ring-request is the request that you would receive in a regular handler • proc is anything you return from on-open. start-ws-connection returns a core.async channel for you • text is the payload, as string 🙂 Some extra nice-to-know • Apart from the on-open event, all return values ​​are ignored • proc is nice to reply to the customer who sent the message • if you want to receive a text from one client, and reply to another client, you will need an global atom holding each proc (or each channel) to make this connection. This may be useful (not using a router, but it is ok to use a router): https://github.com/souenzzo/pedestal-connector-test/blob/main/dev/user/ws.clj

👀 1
souenzzo 2026-05-18T13:12:48.613699Z

I would be very happy to receive your feedback on whether it worked or not 😄

2026-05-18T18:16:52.391079Z

Thank you, I will try it this week and let you know. Is there an easy way to tag the channels with a session id?

2026-05-19T05:05:25.263939Z

ok I worked! a few questions though: • To reply to a client shouldn't I just use the channel in the :on-text ? with (io.pedestal.service.websocket/send-text! channel "some text") ? • Also should I be keeping proc or channel in the my atom of all opened connections? • Also it seems from the code (forgive me my clojure is a little rusty) that you are keeping channels in your atom even when they are closed, won't the atom just grow with the number of connections and interactions with the server?

2026-05-19T06:27:33.829879Z

Also I have no clue how to go about setting up sessions properly 😕

souenzzo 2026-05-19T13:29:19.588609Z

answering: > Is there an easy way to tag the channels with a session id? > Also it seems from the code (forgive me my clojure is a little rusty) that you are keeping channels in your atom even when they are closed, won't the atom just grow with the number of connections and interactions with the server?

(defonce *ws-sessions
  (atom {}))

(defn on-open
  [channel ring-request]
  (let [;; get the "session id" as-if you were in a normal handler.
        session-id (-> ring-request
                     :headers
                     (get "Cookies")
                     cookie->session-id)
        proc (websocket/start-ws-connection channel nil)]
    (swap! *ws-sessions assoc
      session-id {:proc    proc
                  :channel channel})
    proc))

(defn on-close
  [channel proc reason]
  (swap! *ws-sessions
    (fn [ws-sessions]
      (let [;; it might exists a smarter way to cleanup
            session-id (some (fn [[id v]]
                               (when (identical? channel (:channel v))
                                 id))
                         ws-sessions)]
        (when-not session-id
          (log/warn "cant find closing session" channel))
        (dissoc ws-sessions session-id)))))

(defn notify-all!
  []
  (doseq [{:keys [channel]} (vals @*ws-sessions)]
    ;; both are equivalent. All clients will receive both messages
    (websocket/send-text! channel "Hello")
    (async/>!! channel "World")))

souenzzo 2026-05-19T13:32:00.932739Z

Depending on your application, you may not need a "global registry". But operations like "broadcast" won't be possible to implement.

(defn on-open
  [channel ring-request]
  (websocket/start-ws-connection channel nil))
;; a simple implementation that do not require atom or anything global
(defn on-text
  [channel proc text]
  (websocket/send-text! channel (string/upper-case text)))

souenzzo 2026-05-19T13:35:51.440179Z

@hlship If you can, take a look at my examples. I'm also studying and I'm not sure about everything I've said.