Subscriptions Reference

Real-time GraphQL subscriptions for receiving live updates in Unified Commerce Platform.

Overview

Subscriptions enable real-time, bidirectional communication between client and server over WebSockets. When data changes on the server, subscriptions automatically push updates to connected clients.

When to Use Subscriptions

Use subscriptions for:

  • Order status updates
  • Real-time inventory changes
  • Live notifications
  • Chat/messaging
  • Live dashboards

Don't use subscriptions for:

  • One-time data fetching (use queries)
  • Infrequent updates (use polling)
  • Historical data (use queries with pagination)

WebSocket Connection

import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';

// HTTP link for queries and mutations
const httpLink = new HttpLink({
  uri: 'https://gateway.unifiedcommerce.app/graphql',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

// WebSocket link for subscriptions
const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://gateway.unifiedcommerce.app/graphql',
  connectionParams: {
    authToken: 'YOUR_API_KEY',
  },
}));

// Split based on operation type
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});

Order Subscriptions

orderStatusChanged

Subscribe to order status changes.

subscription OrderStatusChanged($orderId: ID!) {
  orderStatusChanged(orderId: $orderId) {
    orderId
    status
    previousStatus
    updatedAt
    updatedBy {
      id
      name
    }
  }
}

Arguments:

NameTypeRequiredDescription
orderIdIDYesOrder ID to watch

Usage:

const { data, loading } = useSubscription(ORDER_STATUS_CHANGED, {
  variables: { orderId: 'ord_123' },
  onData: ({ data }) => {
    console.log('Order status changed:', data.orderStatusChanged);
    // Update UI, show notification, etc.
  }
});

orderCreated

Subscribe to new orders (merchant dashboard).

subscription OrderCreated {
  orderCreated {
    id
    orderNumber
    total
    currency
    status
    customer {
      name
      email
    }
    items {
      product {
        name
      }
      quantity
    }
    createdAt
  }
}

No arguments required (listens to all new orders for your merchant account)

Usage:

useSubscription(ORDER_CREATED, {
  onData: ({ data }) => {
    const order = data.orderCreated;
    // Show notification
    toast.success(`New order #${order.orderNumber} from ${order.customer.name}`);
    // Play sound
    playNotificationSound();
  }
});

orderUpdated

Subscribe to any order updates.

subscription OrderUpdated($orderIds: [ID!]) {
  orderUpdated(orderIds: $orderIds) {
    id
    orderNumber
    status
    total
    updatedAt
    changes {
      field
      oldValue
      newValue
    }
  }
}

Arguments:

NameTypeRequiredDescription
orderIds[ID!]NoSpecific orders to watch (omit to watch all)

Inventory Subscriptions

inventoryChanged

Subscribe to inventory level changes.

subscription InventoryChanged($productIds: [ID!]) {
  inventoryChanged(productIds: $productIds) {
    productId
    variantId
    available
    reserved
    previousAvailable
    warehouseId
    updatedAt
    reason      # e.g., "SALE", "RESTOCK", "ADJUSTMENT"
  }
}

Arguments:

NameTypeRequiredDescription
productIds[ID!]NoSpecific products to watch

Usage:

// Watch specific products
useSubscription(INVENTORY_CHANGED, {
  variables: {
    productIds: ['prod_123', 'prod_456']
  },
  onData: ({ data }) => {
    const inv = data.inventoryChanged;

    if (inv.available === 0) {
      // Product out of stock
      showOutOfStockBadge(inv.productId);
    } else if (inv.available < 10) {
      // Low stock warning
      showLowStockWarning(inv.productId, inv.available);
    }
  }
});

lowStockAlert

Subscribe to low stock alerts.

subscription LowStockAlert {
  lowStockAlert {
    productId
    product {
      name
      sku
    }
    available
    lowStockThreshold
    warehouseId
    alertedAt
  }
}

No arguments (alerts for all products below threshold)


Notification Subscriptions

notificationReceived

Subscribe to user notifications.

subscription NotificationReceived {
  notificationReceived {
    id
    type
    title
    message
    data          # JSON with notification-specific data
    priority
    read
    createdAt
  }
}

Notification Types:

enum NotificationType {
  ORDER_UPDATE
  PAYMENT_RECEIVED
  SHIPMENT_UPDATE
  LOW_STOCK
  CUSTOMER_MESSAGE
  SYSTEM_ALERT
}

Usage:

useSubscription(NOTIFICATION_RECEIVED, {
  onData: ({ data }) => {
    const notification = data.notificationReceived;

    // Show toast notification
    toast.info(notification.title, {
      description: notification.message,
      duration: 5000
    });

    // Update notification badge count
    incrementNotificationCount();

    // Play sound for high-priority notifications
    if (notification.priority === 'HIGH') {
      playNotificationSound();
    }
  }
});

messageReceived

Subscribe to customer messages (chat/support).

subscription MessageReceived($conversationId: ID!) {
  messageReceived(conversationId: $conversationId) {
    id
    conversationId
    sender {
      id
      name
      avatar
    }
    content
    attachments {
      url
      type
      name
    }
    sentAt
  }
}

Arguments:

NameTypeRequiredDescription
conversationIdIDYesConversation ID to watch

Booking Subscriptions

appointmentStatusChanged

Subscribe to appointment status changes.

subscription AppointmentStatusChanged($appointmentId: ID!) {
  appointmentStatusChanged(appointmentId: $appointmentId) {
    appointmentId
    status
    previousStatus
    startTime
    endTime
    updatedAt
  }
}

Arguments:

NameTypeRequiredDescription
appointmentIdIDYesAppointment ID to watch

appointmentReminder

Subscribe to upcoming appointment reminders.

subscription AppointmentReminder {
  appointmentReminder {
    appointment {
      id
      service {
        name
      }
      startTime
      customer {
        name
        phone
      }
    }
    reminderType      # "1_HOUR_BEFORE", "24_HOUR_BEFORE"
  }
}

No arguments (staff member receives reminders for their appointments)


Cart Subscriptions

cartUpdated

Subscribe to cart changes (useful for cart abandonment).

subscription CartUpdated($cartId: ID!) {
  cartUpdated(cartId: $cartId) {
    cartId
    items {
      product {
        name
        price
      }
      quantity
    }
    total
    updatedAt
  }
}

Arguments:

NameTypeRequiredDescription
cartIdIDYesCart ID to watch

Analytics Subscriptions

realtimeMetrics

Subscribe to real-time analytics metrics.

subscription RealtimeMetrics {
  realtimeMetrics {
    timestamp
    activeUsers
    ordersToday
    revenueToday
    averageOrderValue
    conversionRate
  }
}

No arguments (merchant dashboard metrics)

Usage:

useSubscription(REALTIME_METRICS, {
  onData: ({ data }) => {
    const metrics = data.realtimeMetrics;
    updateDashboardCharts(metrics);
  }
});

Payment Subscriptions

paymentStatusChanged

Subscribe to payment status updates.

subscription PaymentStatusChanged($paymentId: ID!) {
  paymentStatusChanged(paymentId: $paymentId) {
    paymentId
    status
    previousStatus
    amount
    currency
    failureReason
    updatedAt
  }
}

Arguments:

NameTypeRequiredDescription
paymentIdIDYesPayment ID to watch

Implementation Examples

React Hook

import { useSubscription } from '@apollo/client';
import { gql } from '@apollo/client';

const ORDER_STATUS_CHANGED = gql`
  subscription OrderStatusChanged($orderId: ID!) {
    orderStatusChanged(orderId: $orderId) {
      orderId
      status
      updatedAt
    }
  }
`;

function OrderTracker({ orderId }) {
  const { data, loading, error } = useSubscription(ORDER_STATUS_CHANGED, {
    variables: { orderId }
  });

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

  return (
    <div>
      <h2>Order Status: {data?.orderStatusChanged.status}</h2>
      <p>Last updated: {data?.orderStatusChanged.updatedAt}</p>
    </div>
  );
}

Vue Composition API

import { useSubscription } from '@vue/apollo-composable';
import gql from 'graphql-tag';

export default {
  setup() {
    const { result, loading, error } = useSubscription(
      gql`
        subscription InventoryChanged($productIds: [ID!]) {
          inventoryChanged(productIds: $productIds) {
            productId
            available
          }
        }
      `,
      { productIds: ['prod_123', 'prod_456'] }
    );

    return {
      inventoryData: result,
      loading,
      error
    };
  }
};

Plain JavaScript

import { createClient } from 'graphql-ws';

const client = createClient({
  url: 'wss://gateway.unifiedcommerce.app/graphql',
  connectionParams: {
    authToken: 'YOUR_API_KEY',
  },
});

// Subscribe
const unsubscribe = client.subscribe(
  {
    query: `
      subscription {
        orderCreated {
          id
          orderNumber
          total
        }
      }
    `,
  },
  {
    next: (data) => {
      console.log('New order:', data.orderCreated);
    },
    error: (error) => {
      console.error('Subscription error:', error);
    },
    complete: () => {
      console.log('Subscription completed');
    },
  }
);

// Unsubscribe later
unsubscribe();

Python

from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport

transport = WebsocketsTransport(
    url='wss://gateway.unifiedcommerce.app/graphql',
    init_payload={
        'authToken': 'YOUR_API_KEY'
    }
)

client = Client(transport=transport)

subscription = gql('''
    subscription OrderStatusChanged($orderId: ID!) {
        orderStatusChanged(orderId: $orderId) {
            orderId
            status
            updatedAt
        }
    }
''')

for result in client.subscribe(
    subscription,
    variable_values={'orderId': 'ord_123'}
):
    print(f"Order status: {result['orderStatusChanged']['status']}")

Go

package main

import (
    "context"
    "fmt"
    "github.com/hasura/go-graphql-client"
)

func main() {
    client := graphql.NewSubscriptionClient("wss://gateway.unifiedcommerce.app/graphql")
    defer client.Close()

    var sub struct {
        OrderStatusChanged struct {
            OrderID  string
            Status   string
            UpdatedAt string
        } `graphql:"orderStatusChanged(orderId: $orderId)"`
    }

    variables := map[string]interface{}{
        "orderId": graphql.ID("ord_123"),
    }

    _, err := client.Subscribe(&sub, variables, func(data []byte, err error) error {
        if err != nil {
            return err
        }
        fmt.Printf("Status: %s\n", sub.OrderStatusChanged.Status)
        return nil
    })

    if err != nil {
        panic(err)
    }

    // Keep connection open
    select {}
}

Connection Management

Authentication

Pass API key in connection params:

const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://gateway.unifiedcommerce.app/graphql',
  connectionParams: {
    authToken: 'YOUR_API_KEY',
  },
}));

Reconnection

Handle connection drops:

const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://gateway.unifiedcommerce.app/graphql',
  connectionParams: {
    authToken: 'YOUR_API_KEY',
  },
  retryAttempts: 5,
  shouldRetry: () => true,
  on: {
    connected: () => console.log('Connected to WebSocket'),
    closed: () => console.log('Disconnected from WebSocket'),
    error: (error) => console.error('WebSocket error:', error),
  },
}));

Cleanup

Always unsubscribe when component unmounts:

useEffect(() => {
  const subscription = client.subscribe({
    query: ORDER_STATUS_CHANGED,
    variables: { orderId }
  }).subscribe({
    next: (data) => {
      // Handle data
    }
  });

  // Cleanup on unmount
  return () => subscription.unsubscribe();
}, [orderId]);

Best Practices

1. Limit Active Subscriptions

Don't create too many concurrent subscriptions:

// ❌ Bad - subscribing to hundreds of orders
orders.forEach(order => {
  useSubscription(ORDER_STATUS_CHANGED, {
    variables: { orderId: order.id }
  });
});

// ✅ Good - subscribe to relevant orders only
const activeOrderIds = orders
  .filter(o => ['PENDING', 'PROCESSING'].includes(o.status))
  .map(o => o.id);

useSubscription(ORDER_UPDATED, {
  variables: { orderIds: activeOrderIds }
});

2. Handle Connection Errors

useSubscription(ORDER_STATUS_CHANGED, {
  variables: { orderId },
  onError: (error) => {
    console.error('Subscription error:', error);
    // Show error to user
    toast.error('Lost connection. Retrying...');
  }
});

3. Use Subscriptions with Queries

Combine query + subscription for initial data + updates:

// Initial data
const { data } = useQuery(GET_ORDER, {
  variables: { orderId }
});

// Real-time updates
useSubscription(ORDER_STATUS_CHANGED, {
  variables: { orderId },
  onData: ({ data: subscriptionData }) => {
    // Update cache
    client.cache.modify({
      id: client.cache.identify({ __typename: 'Order', id: orderId }),
      fields: {
        status: () => subscriptionData.orderStatusChanged.status
      }
    });
  }
});

4. Throttle High-Frequency Updates

import { throttle } from 'lodash';

const handleInventoryUpdate = throttle((data) => {
  updateUI(data);
}, 1000); // Max once per second

useSubscription(INVENTORY_CHANGED, {
  onData: ({ data }) => {
    handleInventoryUpdate(data);
  }
});

Rate Limits

Subscriptions have separate rate limits:

PlanMax Concurrent SubscriptionsMax Messages/Minute
Free5100
Pro501,000
EnterpriseUnlimitedUnlimited

Testing Subscriptions

Mock Subscriptions

import { MockedProvider } from '@apollo/client/testing';

const mocks = [
  {
    request: {
      query: ORDER_STATUS_CHANGED,
      variables: { orderId: 'ord_123' }
    },
    result: {
      data: {
        orderStatusChanged: {
          orderId: 'ord_123',
          status: 'SHIPPED',
          updatedAt: new Date().toISOString()
        }
      }
    }
  }
];

test('renders order status updates', () => {
  render(
    <MockedProvider mocks={mocks}>
      <OrderTracker orderId="ord_123" />
    </MockedProvider>
  );
  // ... assertions
});

Next Steps


Questions about subscriptions? Ask in Discord!

Was this page helpful?