or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client-access.mdindex.mdmutation-operations.mdquery-operations.mdsubscription-operations.mdtesting-utilities.md
tile.json

subscription-operations.mddocs/

Subscription Operations

Handle real-time GraphQL subscriptions for live data updates.

Capabilities

useSubscription Hook

Subscribe to GraphQL subscriptions for real-time data updates and live UI synchronization.

/**
 * Subscribe to GraphQL subscriptions for real-time data updates
 * @param subscription - GraphQL subscription document
 * @param options - Configuration options for the subscription
 * @returns Subscription result with data, loading, and error states
 */
function useSubscription<TData = any, TVariables = OperationVariables>(
  subscription: DocumentNode,
  options?: SubscriptionHookOptions<TData, TVariables>
): SubscriptionResult<TData>;

interface SubscriptionHookOptions<TData, TVariables> {
  /** Variables to pass to the subscription */
  variables?: TVariables;
  /** Skip executing the subscription */
  skip?: boolean;
  /** Fetch policy for subscription */
  fetchPolicy?: FetchPolicy;
  /** Error policy for handling GraphQL errors */
  errorPolicy?: ErrorPolicy;
  /** Context passed to Apollo Link */
  context?: DefaultContext;
  /** Client instance to use */
  client?: ApolloClient<any>;
  /** Callback when subscription data arrives */
  onData?: (options: OnDataOptions<TData>) => void;
  /** Callback when subscription completes */
  onComplete?: () => void;
  /** Callback when subscription errors */
  onError?: (error: ApolloError) => void;
  /** Whether to ignore results */
  ignoreResults?: boolean;
  /** Whether the subscription should re-subscribe */
  shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions<TData, TVariables>) => boolean);
}

interface SubscriptionResult<TData> {
  /** Subscription data */
  data?: TData;
  /** Loading state */
  loading: boolean;
  /** Error state */
  error?: ApolloError;
}

interface OnDataOptions<TData> {
  /** Apollo Client instance */
  client: ApolloClient<object>;
  /** Subscription data */
  data: SubscriptionResult<TData>;
}

Usage Examples:

import { useSubscription, gql } from "@apollo/react-hooks";

const MESSAGE_SUBSCRIPTION = gql`
  subscription OnMessageAdded($chatId: ID!) {
    messageAdded(chatId: $chatId) {
      id
      content
      user {
        id
        name
      }
      createdAt
    }
  }
`;

function ChatMessages({ chatId }: { chatId: string }) {
  const { data, loading, error } = useSubscription(MESSAGE_SUBSCRIPTION, {
    variables: { chatId },
    onData: ({ data }) => {
      console.log("New message received:", data.data?.messageAdded);
    },
    onError: (error) => {
      console.error("Subscription error:", error);
    },
  });

  if (loading) return <div>Connecting to chat...</div>;
  if (error) return <div>Connection error: {error.message}</div>;

  return (
    <div>
      {data?.messageAdded && (
        <div className="new-message">
          <strong>{data.messageAdded.user.name}:</strong>
          {data.messageAdded.content}
        </div>
      )}
    </div>
  );
}

Advanced Usage with Cache Updates:

import { useSubscription, useQuery, gql } from "@apollo/react-hooks";

const GET_MESSAGES = gql`
  query GetMessages($chatId: ID!) {
    messages(chatId: $chatId) {
      id
      content
      user {
        id
        name
      }
      createdAt
    }
  }
`;

const MESSAGE_SUBSCRIPTION = gql`
  subscription OnMessageAdded($chatId: ID!) {
    messageAdded(chatId: $chatId) {
      id
      content
      user {
        id
        name
      }
      createdAt
    }
  }
`;

function ChatRoom({ chatId }: { chatId: string }) {
  // Query existing messages
  const { data: messagesData, loading } = useQuery(GET_MESSAGES, {
    variables: { chatId },
  });

  // Subscribe to new messages
  useSubscription(MESSAGE_SUBSCRIPTION, {
    variables: { chatId },
    onData: ({ client, data }) => {
      if (data.data?.messageAdded) {
        // Update cache with new message
        client.cache.modify({
          fields: {
            messages(existingMessages = [], { readField }) {
              const newMessage = data.data.messageAdded;
              
              // Avoid duplicates
              const exists = existingMessages.some((msgRef: any) => 
                readField('id', msgRef) === newMessage.id
              );
              
              if (!exists) {
                const newMessageRef = client.cache.writeFragment({
                  data: newMessage,
                  fragment: gql`
                    fragment NewMessage on Message {
                      id
                      content
                      user {
                        id
                        name
                      }
                      createdAt
                    }
                  `,
                });
                return [...existingMessages, newMessageRef];
              }
              
              return existingMessages;
            },
          },
        });
      }
    },
  });

  if (loading) return <div>Loading messages...</div>;

  return (
    <div className="chat-room">
      <div className="messages">
        {messagesData?.messages.map((message: any) => (
          <div key={message.id} className="message">
            <strong>{message.user.name}:</strong>
            <span>{message.content}</span>
            <small>{new Date(message.createdAt).toLocaleTimeString()}</small>
          </div>
        ))}
      </div>
    </div>
  );
}

Conditional Subscriptions:

import { useSubscription, gql } from "@apollo/react-hooks";

const USER_STATUS_SUBSCRIPTION = gql`
  subscription OnUserStatusChanged($userId: ID!) {
    userStatusChanged(userId: $userId) {
      id
      status
      lastSeen
    }
  }
`;

function UserStatus({ userId, isOnline }: { userId: string; isOnline: boolean }) {
  const { data, error } = useSubscription(USER_STATUS_SUBSCRIPTION, {
    variables: { userId },
    // Only subscribe when user is online
    skip: !isOnline,
    shouldResubscribe: (options) => {
      // Re-subscribe when variables change
      return options.variables?.userId !== userId;
    },
  });

  if (error) {
    return <div>Status unavailable</div>;
  }

  const status = data?.userStatusChanged?.status || 'unknown';
  
  return (
    <div className={`user-status ${status}`}>
      Status: {status}
    </div>
  );
}

Types

interface BaseSubscriptionOptions<TData, TVariables> {
  variables?: TVariables;
  fetchPolicy?: FetchPolicy;
  errorPolicy?: ErrorPolicy;
  context?: DefaultContext;
}

type FetchPolicy = 
  | 'cache-first'
  | 'cache-only' 
  | 'cache-and-network'
  | 'network-only'
  | 'no-cache'
  | 'standby';

type ErrorPolicy = 'none' | 'ignore' | 'all';