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.

Introduction

Directus provides WebSocket support for real-time bidirectional communication between clients and the server. WebSockets enable instant data updates, live subscriptions, and interactive operations without the overhead of repeated HTTP requests.

WebSocket Endpoints

Directus exposes multiple WebSocket endpoints depending on your configuration:

REST WebSocket

ws://your-directus-instance.com/websocket
wss://your-directus-instance.com/websocket
The main WebSocket endpoint for REST-style operations including subscriptions, CRUD operations, and heartbeat.

GraphQL WebSocket

ws://your-directus-instance.com/graphql
wss://your-directus-instance.com/graphql
WebSocket endpoint for GraphQL subscriptions and operations.
Use wss:// for secure connections (HTTPS sites) and ws:// for local development.

Configuration

Enable and configure WebSocket connections using environment variables:

Enable WebSockets

WEBSOCKETS_ENABLED=true
This is the master switch for all WebSocket functionality.

REST WebSocket Configuration

# Enable REST WebSocket endpoint
WEBSOCKETS_REST_ENABLED=true

# Custom WebSocket path (default: /websocket)
WEBSOCKETS_REST_PATH=/websocket

# Authentication mode: public, handshake, strict
WEBSOCKETS_REST_AUTH=handshake

# Authentication timeout in milliseconds (default: 10000)
WEBSOCKETS_REST_AUTH_TIMEOUT=10000

# Maximum concurrent connections (default: unlimited)
WEBSOCKETS_REST_CONN_LIMIT=100

GraphQL WebSocket Configuration

# Enable GraphQL WebSocket endpoint
WEBSOCKETS_GRAPHQL_ENABLED=true

# Custom WebSocket path (default: /graphql)
WEBSOCKETS_GRAPHQL_PATH=/graphql

# Authentication mode: public, handshake, strict
WEBSOCKETS_GRAPHQL_AUTH=handshake

# Authentication timeout in milliseconds
WEBSOCKETS_GRAPHQL_AUTH_TIMEOUT=10000

Heartbeat Configuration

# Enable WebSocket heartbeat/ping-pong
WEBSOCKETS_HEARTBEAT_ENABLED=true

# Heartbeat interval in milliseconds (default: 30000)
WEBSOCKETS_HEARTBEAT_PERIOD=30000

Logs Configuration

# Enable WebSocket log streaming
WEBSOCKETS_LOGS_ENABLED=false

# Log level: trace, debug, info, warn, error, fatal
WEBSOCKETS_LOGS_LEVEL=info

# Log style: raw or formatted
WEBSOCKETS_LOGS_STYLE=formatted

Authentication Modes

Directus supports three authentication modes for WebSocket connections:

Public Mode

No authentication required. Connections are established with public permissions.
WEBSOCKETS_REST_AUTH=public
Use case: Public data that doesn’t require authentication. Example connection:
const ws = new WebSocket('ws://your-directus-instance.com/websocket');
Clients authenticate after establishing the WebSocket connection by sending an authentication message.
WEBSOCKETS_REST_AUTH=handshake
Use case: Most applications where you want secure WebSocket connections. Example connection:
const ws = new WebSocket('ws://your-directus-instance.com/websocket');

ws.onopen = () => {
  // Authenticate with access token
  ws.send(JSON.stringify({
    type: 'auth',
    access_token: 'your-access-token'
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  
  if (message.type === 'auth' && message.status === 'ok') {
    console.log('Authenticated successfully');
    // Now you can subscribe and send messages
  }
};

Strict Mode

Clients must authenticate before the WebSocket connection is established, using URL parameters or headers.
WEBSOCKETS_REST_AUTH=strict
Use case: High-security environments where unauthenticated connections should be rejected immediately. Example connection:
// Authenticate via URL parameter
const ws = new WebSocket(
  'ws://your-directus-instance.com/websocket?access_token=your-access-token'
);

// Or via session cookie (automatically included by browser)
const ws = new WebSocket('ws://your-directus-instance.com/websocket');

Authentication Methods

When using handshake mode, you can authenticate with different credential types:

Access Token

{
  "type": "auth",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Email and Password

{
  "type": "auth",
  "email": "user@example.com",
  "password": "your-password"
}

Refresh Token

{
  "type": "auth",
  "refresh_token": "your-refresh-token"
}

Message Format

All WebSocket messages use JSON format with the following structure:

Client Messages

{
  type: string;           // Message type (auth, subscribe, items, ping, etc.)
  uid?: string | number;  // Optional unique identifier for tracking responses
  // ... additional fields based on message type
}

Server Responses

{
  type: string;           // Message type
  status: 'ok' | 'error'; // Response status
  uid?: string | number;  // Matches the request uid if provided
  // ... additional fields based on message type
}

Error Responses

{
  "type": "subscribe",
  "status": "error",
  "uid": "request-123",
  "error": {
    "code": "FORBIDDEN",
    "message": "You don't have permission to access this collection."
  }
}

Connection Lifecycle

1. Establish Connection

const ws = new WebSocket('ws://your-directus-instance.com/websocket');

ws.onopen = () => {
  console.log('WebSocket connected');
};

2. Authenticate (if using handshake mode)

ws.send(JSON.stringify({
  type: 'auth',
  access_token: 'your-token'
}));

3. Wait for Authentication Response

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  
  if (message.type === 'auth' && message.status === 'ok') {
    console.log('Authenticated');
    // Proceed with operations
  }
};

4. Send Messages and Subscribe

// Now you can send messages, subscribe to collections, etc.
ws.send(JSON.stringify({
  type: 'subscribe',
  collection: 'articles',
  uid: 'subscription-1'
}));

5. Handle Disconnection

ws.onclose = (event) => {
  console.log('WebSocket disconnected', event.code, event.reason);
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

Heartbeat / Ping-Pong

When WEBSOCKETS_HEARTBEAT_ENABLED=true, the server sends periodic ping messages to keep connections alive:
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  
  if (message.type === 'ping') {
    // Respond with pong
    ws.send(JSON.stringify({ type: 'pong' }));
  }
};
Most WebSocket clients handle ping/pong automatically. You typically don’t need to implement this manually unless you’re using a custom client.

Rate Limiting

If rate limiting is enabled for the Directus instance, it also applies to WebSocket messages:
RATE_LIMITER_ENABLED=true
When rate limited, you’ll receive an error response:
{
  "type": "server",
  "status": "error",
  "error": {
    "code": "REQUESTS_EXCEEDED",
    "message": "Too many messages, retry after 1000ms."
  }
}

Token Expiration

WebSocket connections automatically handle token expiration:
  1. When your access token is about to expire, the server sends an error
  2. You have a grace period to re-authenticate (default: 10 seconds)
  3. Send a new auth message with valid credentials
  4. If you don’t re-authenticate in time, the connection is closed (in non-public mode)
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  
  if (message.type === 'auth' && message.status === 'error') {
    if (message.error.code === 'TOKEN_EXPIRED') {
      // Re-authenticate
      ws.send(JSON.stringify({
        type: 'auth',
        refresh_token: 'your-refresh-token'
      }));
    }
  }
};

Permission Handling

All WebSocket operations respect user permissions:
  • Subscriptions only receive updates for items the user can read
  • Item operations (create, read, update, delete) are subject to role permissions
  • Permission changes take effect immediately for active connections
  • Unauthorized operations return error responses

Basic Example

Here’s a complete example of connecting and authenticating:
const ws = new WebSocket('ws://your-directus-instance.com/websocket');

ws.onopen = () => {
  console.log('Connected to WebSocket');
  
  // Authenticate
  ws.send(JSON.stringify({
    type: 'auth',
    email: 'user@example.com',
    password: 'password'
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('Received:', message);
  
  if (message.type === 'auth' && message.status === 'ok') {
    console.log('Authenticated successfully');
    
    // Subscribe to a collection
    ws.send(JSON.stringify({
      type: 'subscribe',
      collection: 'articles',
      uid: 'sub-1'
    }));
  }
  
  if (message.type === 'subscription') {
    console.log('Subscription event:', message.event, message.data);
  }
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
  console.log('Disconnected:', event.code, event.reason);
};

Error Codes

Common WebSocket error codes:
CodeDescription
AUTH_FAILEDAuthentication credentials are invalid
AUTH_TIMEOUTAuthentication took too long
TOKEN_EXPIREDAccess token has expired
INVALID_PAYLOADMessage format is invalid
INVALID_COLLECTIONCollection doesn’t exist or isn’t accessible
FORBIDDENNo permission for the requested operation
REQUESTS_EXCEEDEDRate limit exceeded

Best Practices

Always include a uid field in your messages to track responses:
ws.send(JSON.stringify({
  type: 'subscribe',
  collection: 'articles',
  uid: 'subscription-1' // Track this subscription
}));
Implement automatic reconnection logic for production:
let reconnectAttempts = 0;
const maxReconnectAttempts = 10;

function connect() {
  const ws = new WebSocket('ws://your-instance.com/websocket');
  
  ws.onclose = () => {
    if (reconnectAttempts < maxReconnectAttempts) {
      reconnectAttempts++;
      const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
      setTimeout(connect, delay);
    }
  };
  
  ws.onopen = () => {
    reconnectAttempts = 0;
  };
}
Always validate incoming messages before using them:
ws.onmessage = (event) => {
  try {
    const message = JSON.parse(event.data);
    
    if (!message.type || !message.status) {
      console.warn('Invalid message format');
      return;
    }
    
    // Process message...
  } catch (error) {
    console.error('Failed to parse message:', error);
  }
};
Prefer handshake authentication over strict mode for better security:
# Recommended
WEBSOCKETS_REST_AUTH=handshake

# Avoid passing tokens in URL
WEBSOCKETS_REST_AUTH=strict
Always close WebSocket connections when done:
// Before page unload
window.addEventListener('beforeunload', () => {
  ws.close(1000, 'Page unload');
});

Troubleshooting

Ensure WebSockets are enabled:
WEBSOCKETS_ENABLED=true
WEBSOCKETS_REST_ENABLED=true
Check your authentication mode and credentials:
  • Verify the WEBSOCKETS_REST_AUTH setting
  • Ensure your access token is valid
  • Check that the user has the required permissions
Increase the authentication timeout:
WEBSOCKETS_REST_AUTH_TIMEOUT=30000
Adjust the connection limit:
WEBSOCKETS_REST_CONN_LIMIT=200

Next Steps

WebSocket Subscriptions

Subscribe to real-time collection updates

SDK Real-time

Use the Directus SDK for WebSocket operations

GraphQL Subscriptions

Real-time updates with GraphQL