WebSocket

Client Integration

Use socket.js to connect any web page or React app to the notification service. Include the script, connect, register your identity, and listen for messages.

1. Getting the script

Plain HTML

Add the script tag to your page before your own code. It exposes NotificationSocket as a global.

<script src="https://notification.magicrunez.com/assets/socket.js"></script>
React / bundled app

Add the same script tag to your index.html. NotificationSocket will be available as window.NotificationSocket once the script loads.

2. Creating a connection

Pass the server URL to the constructor. The connection opens immediately. If it drops it retries automatically every 2 seconds.

const socket = new NotificationSocket('ws://notification.magicrunez.com');
In React

Create the socket inside a useEffect so it opens on mount and closes cleanly on unmount. Call register() straight away so the server knows who this client is.

useEffect(() => {
  const socket = new NotificationSocket('ws://notification.magicrunez.com');

  socket.register(userId, userGroups, tenantId);

  // attach event handlers here (see step 3)

  return () => socket.destroy(); // close cleanly on unmount
}, []);
Always call destroy() in your cleanup function. It closes the connection and stops the auto-reconnect loop so the old socket doesn't keep running in the background.

3. Watching for messages and setting state

Use socket.on(event, handler) to react to incoming messages. In React, register handlers inside the same useEffect so they have access to your state setters.

const [connected, setConnected]         = useState(false);
const [notifications, setNotifications] = useState([]);

useEffect(() => {
  const socket = new NotificationSocket('ws://notification.magicrunez.com');
  socket.register(userId, userGroups, tenantId);

  socket.on('open',  () => setConnected(true));
  socket.on('close', () => setConnected(false));

  socket.on('message', (msg) => {
    setNotifications(prev => [msg, ...prev]);
  });

  return () => socket.destroy();
}, []);

Filtering by message type

The msg.type field describes the kind of message. Use it to update only the relevant slice of state.

socket.on('message', (msg) => {
  if (msg.type === 'alert') {
    setAlerts(prev => [msg, ...prev]);
  }
  if (msg.type === 'chat') {
    setChatMessages(prev => [...prev, msg]);
  }
});

Available events

EventWhen it fires
openConnection established (including after a reconnect)
closeConnection dropped — socket will retry automatically
errorA WebSocket error occurred
connectedServer acknowledged the connection
registeredServer confirmed your identity
messageA message was routed to this client

Inbound message shape

{
  event:   "message",
  from:    { tenantId: "…", userId: "42" },
  to:      [{ type: "user", id: "99" }],
  type:    "alert",
  message: "Maintenance starting shortly",
  data:    "{\"startsIn\":300}"   // always a JSON string
}

data is always a JSON string. Parse it with JSON.parse(msg.data) when you need the object.

4. Advanced: Sending messages

Most apps only receive messages. If your app also needs to send them, use socket.send().

Sending a message

socket.send(
  [
    { type: 'user',  id: '99' },
    { type: 'group', id: ['admins', 'support'] }
  ],
  'alert',                         // type / label
  'Maintenance starting shortly',  // human-readable text
  { startsIn: 300 }                // optional data payload
);

Each recipient has a type of 'user' or 'group' and an id that is either a single string or an array of strings for multiple recipients in one entry. Messages sent before the connection is open are queued and delivered automatically once connected.

Switching servers at runtime

Store the URL in state and re-run the useEffect when it changes. The old socket is destroyed and a fresh one is created.

const [serverUrl, setServerUrl] = useState('ws://notification.magicrunez.com');

useEffect(() => {
  const socket = new NotificationSocket(serverUrl);
  socket.register(userId, userGroups, tenantId);
  socket.on('message', (msg) => setNotifications(prev => [msg, ...prev]));
  return () => socket.destroy();
}, [serverUrl]); // re-runs whenever the URL changes

5. TypeScript types

Copy these interfaces into your project (e.g. notifications.d.ts) to get full type safety when working with the notification service.

Core types

/** A single recipient entry — target one or many ids of the same type */
type Recipient = {
  type: 'user' | 'group';
  id:   string | string[];
};

/** Shape of a message you send (WebSocket or REST body) */
type OutboundMessage = {
  to:      Recipient[];
  type:    string;    // e.g. 'alert' | 'chat' — your own labels
  message: string;    // human-readable text
  data?:   string;    // optional JSON string — use JSON.stringify(payload)
};

/** Shape of a message the server routes to this client */
type InboundMessage = {
  event:   'message';
  from:    { tenantId: string; userId: string };
  to:      Recipient[];
  type:    string;
  message: string;
  data:    string;    // always a JSON string — parse with JSON.parse(msg.data)
};

Message shape examples

// Outbound — sent via socket.send() or the REST API
const body: OutboundMessage = {
  to: [
    { type: 'user',  id: '1' },
    { type: 'group', id: ['1', '8'] }
  ],
  type:    'alert',
  message: 'Server-side push',
  data:    JSON.stringify({ priority: 'high' }),
};

// Inbound — received via socket.on('message', …)
// {
//   event:   "message",
//   from:    { tenantId: "acme", userId: "42" },
//   to:      [{ type: "user", id: "1" }],
//   type:    "alert",
//   message: "Server-side push",
//   data:    "{\"priority\":\"high\"}"
// }

Typed React hook

const [notifications, setNotifications] = useState<InboundMessage[]>([]);
const [connected,     setConnected]     = useState<boolean>(false);

useEffect(() => {
  const socket = new (window as any).NotificationSocket('ws://notification.magicrunez.com');
  socket.register(userId, userGroups, tenantId);

  socket.on('open',  () => setConnected(true));
  socket.on('close', () => setConnected(false));

  socket.on('message', (msg: InboundMessage) => {
    const parsed = msg.data ? JSON.parse(msg.data) : null;
    setNotifications(prev => [{ ...msg, _parsed: parsed }, ...prev]);
  });

  // Sending a typed outbound message
  const outbound: OutboundMessage = {
    to:      [{ type: 'user', id: '99' }],
    type:    'alert',
    message: 'Maintenance starting shortly',
    data:    JSON.stringify({ startsIn: 300 }),
  };
  socket.send(outbound.to, outbound.type, outbound.message, JSON.parse(outbound.data!));

  return () => socket.destroy();
}, []);
NotificationSocket is loaded via a plain <script> tag and is not a typed npm package. Cast it through (window as any).NotificationSocket, or add a minimal ambient declaration:

declare const NotificationSocket: new (url: string) => any;