Liking cljdoc? Tell your friends :D

realtime

WebSocket-based real-time communication with JWT authentication, message routing, and topic-based pub/sub.

Key namespaces

NamespacePurpose

wagoe.realtime.core.connection

Pure: connection records, authorization, filtering

wagoe.realtime.core.message

Pure: message creation, routing logic

wagoe.realtime.core.auth

Pure: JWT extraction, claims validation, permission checks

wagoe.realtime.core.pubsub

Pure: topic subscription management

wagoe.realtime.ports

Protocols: IRealtimeService, IConnectionRegistry, IWebSocketConnection, IJWTVerifier, IPubSubManager

wagoe.realtime.shell.service

Service orchestrating core + adapters

wagoe.realtime.shell.connection-registry

In-memory registry (atom-backed)

wagoe.realtime.shell.pubsub-manager

Atom-backed pub/sub state management

wagoe.realtime.shell.adapters.websocket-adapter

Ring/Jetty WebSocket adapter

wagoe.realtime.shell.adapters.jwt-adapter

JWT verifier delegating to wagoe-user

Message routing types

TypeTargetExample use case

:broadcast

All connections

System announcement

:user

Specific user-id

Direct message or notification

:role

Users with a specific role

Admin-only alert

:connection

Specific connection-id

Job progress update

Usage

(require '[wagoe.realtime.ports :as ports])

;; Broadcast
(ports/send-to-all service {:type :announcement :text "Maintenance in 5 minutes"})

;; To specific user
(ports/send-to-user service user-id {:type :notification :data {...}})

;; Topic pub/sub
(ports/subscribe-to-topic pubsub conn-id "order:123")
(ports/publish-to-topic service "order:123" {:type :order-updated :payload {...}})

Server-side subscribers

A topic subscriber does not have to be a browser. subscribe-service registers an in-process function, so the same publish reaches connected clients and code running in the server:

(def sub-id
  (ports/subscribe-service pubsub "order:events"
    (fn [message]
      (create-notification! (:payload message)))))

(ports/publish-to-topic service "order:events"
                        {:type "created" :payload {:id 1}})
;; => 4   ; three open sockets and this handler

(ports/unsubscribe-service pubsub sub-id)   ; => true

This is what lets an application use realtime as its internal event bus rather than running a second pub/sub alongside it for server-to-server messages.

What to know before relying on it:

  • Handlers are node-local. The function lives in one JVM and cannot be relayed, so it is registered on the node that will run it. Under the :redis provider the message is fanned out to every node and each node invokes its own handlers — a handler registered once therefore runs once, whichever node published.

  • They run on the delivery thread. Keep them quick; hand slow or failure- prone work to wagoe-jobs.

  • A handler that throws is logged and skipped. It cannot stop the handlers after it, and it cannot stop delivery to sockets.

  • publish-to-topic counts them. A topic with three handlers and no open sockets returns 3, not 0.

  • Order between handlers is unspecified. If two subscribers must run in a fixed order, that is one subscriber calling two things.

Connection lifecycle

  1. Client connects: ws://host/ws?token=<jwt>

  2. Server verifies JWT via IJWTVerifier

  3. Connection registered in registry

  4. Client sends/receives messages

  5. On disconnect: cleanup + unsubscribe from all topics

Testing

clojure -M:test :realtime

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close