"Subscriptions"

Subscribe to queries and receive realtime updates when data changes.

How subscriptions work

Tether uses WebSocket connections to push updates to clients. When you subscribe to a query, you receive the initial data immediately, then automatic updates whenever a mutation affects the subscribed data.

Flow:

  1. Client subscribes to query — the client opens a WebSocket connection and registers a subscription.
  2. Initial data returned — the server immediately responds with the current query result.
  3. Updates pushed on changes — whenever a mutation affects the subscribed data, the server pushes an update.

Subscribing in Vue/Nuxt

Use the useQuery composable for reactive subscriptions. It's auto-imported by the Nuxt module:

// This automatically subscribes and updates when data changes
const { data, isLoading, error } = useQuery('todos.list', { completed: false });

Subscribing in React

Use the vanilla client's subscribe method with React's useEffect. The React SDK is in development.

import { useEffect, useState } from 'react';
import { tether } from '../lib/tether';

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = tether.subscribe(
      'todos.list',
      { completed: false },
      (data) => {
        setTodos(data);
        setLoading(false);
      }
    );
    return () => unsubscribe();
  }, []);

  if (loading) return <p>Loading...</p>;

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

Manual subscriptions

For more control, use the low-level subscription API:

import { TetherClient } from '@tthr/client';

const client = new TetherClient({
  url: 'https://tether-api.yourdomain.com',
  projectId: 'your-project-id',
  authToken: () => getAuthToken(), // optional
});

// Subscribe to a query
const unsubscribe = client.subscribe(
  'todos.list',
  { completed: false },
  (data) => {
    console.log('Todos updated:', data);
  }
);

// Unsubscribe when done
unsubscribe();

Update behaviour

Subscription updates are complete snapshots, not diffs. When data changes, you receive the full query result. This simplifies client-side state management and ensures consistency.

Connection resilience

Tether automatically handles connection issues with built-in resilience features.

Heartbeat

The client sends periodic ping messages every 30 seconds to detect connection health. If no response is received within 10 seconds, the client automatically reconnects.

Automatic reconnection

On disconnection, the client reconnects with exponential backoff:

Attempt Delay
Attempt 1 1 second
Attempt 2 2 seconds
Attempt 3 4 seconds
Attempt 4 8 seconds
Attempt 5 16 seconds

Sync on reconnect

When reconnecting, the client sends its lastSyncTime to receive any changes that occurred while disconnected. The server maintains a change queue with:

  • Maximum 1000 entries per project
  • Maximum age of 5 minutes
  • Per-query isolation

For disconnections longer than 5 minutes, clients receive fresh data without change replay.

Versioning

Each data message includes a version number. Clients can detect missed updates if versions are non-sequential:

{
  "type": "data",
  "id": "sub1",
  "data": [...],
  "meta": { "version": 42 }
}