Custom WebSocket Handlers
The other pages in this section cover client-side WebSocket usage - authenticating, subscribing to changes, and performing CRUD actions. This page covers the server side: using hook extensions to react to connections, handle custom message types, and push data to clients.
Before You Start
You will need:
- WebSockets enabled in your Directus project (
WEBSOCKETS_ENABLED=true) - see Realtime configuration - Familiarity with hook extensions - see the extensions quickstart and hooks reference
React to Connection Events
Use action hooks to run logic when clients connect, disconnect, or authenticate. The callback receives a client object representing the WebSocket connection.
The client object has the following key properties:
| Property | Description |
|---|---|
uid | Unique identifier for the connection. |
send(data) | Send a string message to the client. |
accountability | Object containing user and role IDs after authentication. |
export default ({ action }) => {
action('websocket.connect', ({ client }) => {
console.log(`Client connected: ${client.uid}`);
});
action('websocket.close', ({ client }) => {
console.log(`Client disconnected: ${client.uid}`);
});
action('websocket.auth.success', ({ client }) => {
console.log(`Authenticated: user ${client.accountability?.user}`);
});
};
The websocket.auth.failure and websocket.error action events are also available for handling failed authentication attempts and connection errors.
Handle Custom Message Types
Use an action on websocket.message to detect custom message types sent by clients. The message parameter is already parsed from JSON - you do not need to call JSON.parse().
export default ({ action }) => {
action('websocket.message', ({ message, client }) => {
if (message.type !== 'typing-indicator') return;
console.log(`User ${client.accountability?.user} is typing in ${message.channel}`);
});
};
This action runs after Directus has processed the message. To modify or block messages before processing, use a filter instead.
Intercept and Modify Messages
Use a filter on websocket.message to transform messages before Directus processes them. Filters receive the parsed message as the payload and must return it for processing to continue. To block a message entirely, throw an error.
export default ({ filter }) => {
filter('websocket.message', (message) => {
if (message.type === 'subscribe' && !message.query?.fields) {
message.query = { ...message.query, fields: ['id', 'date_created'] };
}
return message;
});
};
Push Messages to Clients
The WebSocketService is available through the hook's context and provides access to all connected clients. Use clients() to get the full set, or broadcast() to send a message to all (or a filtered subset of) clients.
export default ({ action }, { services }) => {
action('items.create', ({ payload, collection }) => {
if (collection !== 'notifications') return;
const wsService = new services.WebSocketService();
wsService.broadcast(JSON.stringify({ type: 'notification', data: payload }));
});
};
The broadcast method accepts an optional second argument to target specific users or roles:
wsService.broadcast(
JSON.stringify({ type: 'notification', data: payload }),
{ user: '1a2b3c4d-...' }
);
If you need more control over which clients receive the message, use clients() to get the Set of connected clients and iterate over them directly:
const wsService = new services.WebSocketService();
for (const client of wsService.clients()) {
if (client.accountability?.role === 'admin-role-id') {
client.send(JSON.stringify({ type: 'alert', data: payload }));
}
}
WebSocketService only provides access to REST WebSocket clients. GraphQL subscription and logs streaming clients are managed by separate controllers and are not accessible through this service.Next Steps
- Hooks reference - full list of filter and action events
- Extensions quickstart - set up your first extension
- Realtime configuration - WebSocket environment variables
- Subscriptions and Actions - client-side WebSocket usage
Get once-a-month release notes & real‑world code tips...no fluff. 🐰