Subscribe to an observable query
Your server already streams changes from an observable query. Now a client has to listen. Pick the transport that matches the client you have: a browser EventSource, a raw WebSocket, or the @cratis/arc client with generated proxies.
Before you start
Section titled “Before you start”- A running Arc server that serves an observable query. The examples use the Tasks sample on
127.0.0.1:3000and its/api/tasks/listing/observe-all-tasksroute; Get started shows how to run it. Replace the route with your own query’s route from/.cratis/queries. - To check the route from a terminal first, follow Using observable queries with curl.
Subscribe over direct server-sent events
Section titled “Subscribe over direct server-sent events”In a browser on the same origin:
const stream = new EventSource('/api/tasks/listing/observe-all-tasks');stream.onmessage = event => console.log(JSON.parse(event.data).data);// Call stream.close() when you no longer need updates.A direct SSE frame is data: <query result JSON>\n\n, a full query result each time. Browser EventSource cannot set an Authorization header: authenticate with your host’s session cookie, never with the .cratis-identity display cookie.
Subscribe over a direct WebSocket
Section titled “Subscribe over a direct WebSocket”const socket = new WebSocket(`ws://${location.host}/api/tasks/listing/observe-all-tasks`);socket.onmessage = event => { const message = JSON.parse(event.data); if (message.type === 'Data') console.log(message.data.data); if (message.type === 'Pong') console.log('Pong at', message.timestamp);};socket.onopen = () => socket.send(JSON.stringify({ type: 'Ping', timestamp: Date.now() }));// Call socket.close() when you no longer need updates.Direct WebSocket frames are {"type":"Data","data":<query result>}; a Ping receives a Pong with the same timestamp. The standalone Node host accepts upgrades on its own; framework adapters need a separate mount, described in WebSockets.
Use the installed client
Section titled “Use the installed client”The published @cratis/arc client subscribes through generated ObservableQueryFor proxies over the multiplexed hub. The plain client defaults to the WebSocket hub. The <Arc> provider from @cratis/arc.react defaults to the SSE hub instead; both accept anonymous connections. Each subscription still passes through query authorization, so an anonymous caller can only observe queries that permit anonymous access. The default <Arc> configuration works for the Tasks browser example without switching transports.
For the direct transports above, set these before subscribing:
import { Globals } from '@cratis/arc';import { QueryTransportMethod } from '@cratis/arc/queries';
Globals.queryDirectMode = true;Globals.queryTransportMethod = QueryTransportMethod.ServerSentEvents; // or QueryTransportMethod.WebSocketGenerated proxies carry the exact query name. The proxy generator emits them for model-bound observable queries.
When a subscription ends
Section titled “When a subscription ends”Every transport ends the same way: when the client closes or disconnects, when the source completes or errors, or when an emission guard denies an emission. Subscription lifetime describes what Arc cleans up.
Next steps
Section titled “Next steps”- Multiplexed observable queries explains the hubs that generated clients use.
- Frontend usage shows generated proxies in a React application.
- Testing observable queries collects emissions in a spec, without a transport.