Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/directus/directus/llms.txt

Use this file to discover all available pages before exploring further.

Directus provides real-time capabilities through WebSocket connections, enabling live updates, subscriptions, and collaborative editing features.

WebSocket Connection

Connect to Directus via WebSocket for bidirectional communication:
import { createDirectus, realtime } from '@directus/sdk';

const client = createDirectus('https://your-project.directus.app')
  .with(realtime());

// Connect to WebSocket
await client.connect();

// Connection is now established

WebSocket URL

The WebSocket endpoint is available at:
wss://your-project.directus.app/websocket
For local development with HTTP:
ws://localhost:8055/websocket

Authentication

Authenticate your WebSocket connection:

Using Email & Password

const client = createDirectus('https://your-project.directus.app')
  .with(realtime());

await client.connect();

await client.sendMessage({
  type: 'auth',
  email: 'user@example.com',
  password: 'password'
});

Using Access Token

await client.sendMessage({
  type: 'auth',
  access_token: 'your-access-token'
});

Using Refresh Token

await client.sendMessage({
  type: 'auth',
  refresh_token: 'your-refresh-token'
});

Subscriptions

Subscribe to real-time updates for collections:

Subscribe to Collection

const { subscription } = await client.subscribe('articles', {
  query: {
    filter: {
      status: { _eq: 'published' }
    },
    fields: ['id', 'title', 'author.email']
  }
});

// Listen for updates
for await (const message of subscription) {
  console.log('Update:', message);
  
  if (message.event === 'init') {
    console.log('Initial data:', message.data);
  }
  
  if (message.event === 'create') {
    console.log('New item:', message.data);
  }
  
  if (message.event === 'update') {
    console.log('Updated item:', message.data);
  }
  
  if (message.event === 'delete') {
    console.log('Deleted items:', message.data);
  }
}

Subscription Events

WebSocket subscriptions emit different event types:
  • init - Initial data when subscription starts
  • create - New items matching the filter
  • update - Items updated to match the filter
  • delete - Items deleted or no longer match filter

Subscription Message Format

interface SubscriptionMessage {
  type: 'subscription';
  event: 'init' | 'create' | 'update' | 'delete';
  data: any[];
  uid?: string | number;
}

Live Queries

Execute live queries that update automatically:
// Subscribe with advanced query
const { subscription } = await client.subscribe('articles', {
  query: {
    filter: {
      _and: [
        { status: { _eq: 'published' } },
        { date_created: { _gte: '$NOW(-7 days)' } }
      ]
    },
    fields: ['*', 'author.*', 'categories.*'],
    sort: ['-date_created'],
    limit: 20
  }
});

for await (const message of subscription) {
  // Real-time filtered results
  updateUI(message.data);
}

Heartbeat

Maintain connection with heartbeat pings:
// Send heartbeat
await client.sendMessage({
  type: 'ping'
});

// Receive heartbeat response
// { type: 'pong' }
The SDK automatically handles heartbeats to keep the connection alive.

Connection Management

Connection Events

client.onWebSocket('open', () => {
  console.log('WebSocket connected');
});

client.onWebSocket('error', (error) => {
  console.error('WebSocket error:', error);
});

client.onWebSocket('close', () => {
  console.log('WebSocket disconnected');
});

client.onWebSocket('message', (message) => {
  console.log('Received:', message);
});

Disconnect

// Close WebSocket connection
await client.disconnect();

Reconnection

The SDK automatically attempts to reconnect on connection loss:
// Reconnection happens automatically
// Subscriptions are re-established after reconnect

Collaborative Editing

Directus supports collaborative editing with operational transformation:
// Subscribe to item for collaboration
const { subscription } = await client.subscribe('articles', {
  query: {
    filter: { id: { _eq: 'article-id' } }
  }
});

// Listen for changes from other users
for await (const message of subscription) {
  if (message.event === 'update') {
    // Apply changes to editor
    applyRemoteChanges(message.data);
  }
}

// Send local changes
await client.request(
  updateItem('articles', 'article-id', {
    content: updatedContent
  })
);

Presence

Track online users and their activity:
// Track who's viewing an item
const presenceData = {
  collection: 'articles',
  item: 'article-id',
  user: userId,
  timestamp: Date.now()
};

// Store presence in separate collection
await client.request(
  createItem('presence', presenceData)
);

// Subscribe to presence updates
const { subscription } = await client.subscribe('presence', {
  query: {
    filter: {
      collection: { _eq: 'articles' },
      item: { _eq: 'article-id' },
      timestamp: { _gte: '$NOW(-5 minutes)' }
    }
  }
});

Real-Time Notifications

Implement live notification system:
// Subscribe to user's notifications
const { subscription } = await client.subscribe('directus_notifications', {
  query: {
    filter: {
      recipient: { _eq: currentUserId },
      status: { _eq: 'unread' }
    }
  }
});

for await (const message of subscription) {
  if (message.event === 'create') {
    // Show notification toast
    showNotification(message.data[0]);
  }
}

Activity Feed

Create real-time activity streams:
const { subscription } = await client.subscribe('directus_activity', {
  query: {
    filter: {
      collection: { _eq: 'articles' },
      action: { _in: ['create', 'update', 'delete'] }
    },
    fields: ['*', 'user.first_name', 'user.last_name'],
    sort: ['-timestamp'],
    limit: 50
  }
});

for await (const message of subscription) {
  if (message.event === 'create') {
    // Add to activity feed
    addActivityItem(message.data[0]);
  }
}

Live Dashboard

Build real-time dashboards:
// Subscribe to multiple collections
const articlesSubscription = await client.subscribe('articles', {
  query: {
    aggregate: { count: '*' },
    filter: { status: { _eq: 'published' } }
  }
});

const usersSubscription = await client.subscribe('directus_users', {
  query: {
    aggregate: { count: '*' },
    filter: { status: { _eq: 'active' } }
  }
});

// Update dashboard in real-time
for await (const message of articlesSubscription.subscription) {
  updateMetric('articles', message.data);
}

Performance Considerations

Filter Subscriptions

Always use filters to limit subscription data:
// ✅ Good - filtered subscription
await client.subscribe('articles', {
  query: {
    filter: { author: { _eq: currentUserId } }
  }
});

// ❌ Bad - subscribing to all items
await client.subscribe('articles');

Limit Fields

Request only needed fields:
await client.subscribe('articles', {
  query: {
    fields: ['id', 'title', 'status'] // Only what you need
  }
});

Unsubscribe When Done

const { subscription, unsubscribe } = await client.subscribe('articles');

// Later...
await unsubscribe();

WebSocket Configuration

Server Configuration

# Enable WebSocket
WEBSOCKETS_ENABLED=true

# GraphQL subscriptions over WebSocket
WEBSOCKETS_GRAPHQL_ENABLED=true

# Heartbeat interval (ms)
WEBSOCKETS_HEARTBEAT_PERIOD=30000

# Authentication timeout (ms)
WEBSOCKETS_AUTH_TIMEOUT=10000

Error Handling

try {
  const { subscription } = await client.subscribe('articles');
  
  for await (const message of subscription) {
    if (message.type === 'error') {
      console.error('Subscription error:', message.error);
    }
  }
} catch (error) {
  console.error('Connection error:', error);
}

Best Practices

Only subscribe to data you need. Use filters to reduce bandwidth and processing.
Implement UI feedback for connection state and handle reconnection gracefully.
Clean up subscriptions when components unmount or data is no longer needed.
Enable heartbeats to detect stale connections and reconnect automatically.