WordPress Development Recipe: Real-time custom event triggers using WebSockets and Rewrite API custom endpoints
Leveraging WordPress Rewrite API for Custom Endpoints
To facilitate real-time event triggering within a WordPress environment, we’ll establish custom API endpoints using the WordPress Rewrite API. This approach allows us to define specific URL structures that map to custom PHP logic, acting as the ingress point for our event notifications. We’ll register a custom rule that intercepts requests to a designated endpoint, such as /wp-json/myplugin/v1/event-trigger.
The core of this mechanism involves two key WordPress hooks: rewrite_rules_array to add our custom rule and template_redirect to hook into the request lifecycle and execute our handler function. This ensures that our custom endpoint is recognized by WordPress’s routing system and that our logic is invoked when a request matches the defined pattern.
Registering the Rewrite Rule
The following PHP snippet demonstrates how to register a custom rewrite rule. This code should be placed within your plugin’s main file or an included file that’s loaded on every WordPress page load.
add_filter( 'rewrite_rules_array', 'myplugin_add_rewrite_rules' );
function myplugin_add_rewrite_rules( $rules ) {
$new_rules = array(
'myplugin/v1/event-trigger$' => 'index.php?myplugin_event=1',
);
return array_merge( $new_rules, $rules );
}
add_filter( 'query_vars', 'myplugin_add_query_vars' );
function myplugin_add_query_vars( $vars ) {
$vars[] = 'myplugin_event';
return $vars;
}
add_action( 'template_redirect', 'myplugin_handle_event_trigger' );
function myplugin_handle_event_trigger() {
if ( get_query_var( 'myplugin_event' ) ) {
// Prevent WordPress from loading a template
status_header( 200 );
// Set content type for JSON response
header( 'Content-Type: application/json' );
// Placeholder for actual event processing logic
$response_data = array(
'status' => 'success',
'message' => 'Event received and processed.',
'timestamp' => current_time( 'mysql' )
);
echo json_encode( $response_data );
exit; // Crucial to stop further WordPress execution
}
}
// Flush rewrite rules on plugin activation/deactivation
register_activation_hook( __FILE__, 'myplugin_flush_rewrite_rules' );
function myplugin_flush_rewrite_rules() {
myplugin_add_rewrite_rules( array() ); // Ensure rules are added
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'myplugin_flush_rewrite_rules_deactivate' );
function myplugin_flush_rewrite_rules_deactivate() {
// Remove the custom rule if it exists
global $wp_rewrite;
$rules = $wp_rewrite->rules;
foreach( $rules as $rule => $match ) {
if ( strpos( $rule, 'myplugin/v1/event-trigger' ) !== false ) {
unset( $wp_rewrite->rules[$rule] );
}
}
flush_rewrite_rules();
}
After adding this code, you must flush WordPress rewrite rules. The easiest way to do this is by deactivating and then reactivating your plugin. Alternatively, you can manually trigger this by visiting the WordPress Admin -> Settings -> Permalinks page. The myplugin_flush_rewrite_rules function ensures this happens automatically upon activation.
Integrating WebSockets for Real-time Communication
While the custom endpoint serves as the trigger mechanism, it’s a one-way communication. For true real-time, bi-directional communication, WebSockets are the ideal solution. We’ll use a dedicated WebSocket server, which can be a separate application (e.g., Node.js with Socket.IO or Ratchet for PHP) or a managed service. The WordPress plugin will act as a bridge, pushing events to the WebSocket server when our custom endpoint is hit.
Consider a scenario where a user action in the WordPress admin (e.g., publishing a post) needs to trigger an update on a frontend dashboard without a page refresh. The custom endpoint will be called by an external service or a frontend JavaScript making an AJAX request. Upon receiving this request, our PHP handler will then push a message to the WebSocket server.
WebSocket Server Implementation (Conceptual – Node.js Example)
Here’s a simplified conceptual example of a Node.js WebSocket server using Socket.IO. This server listens for messages from WordPress and broadcasts them to connected clients.
// server.js (Node.js with Socket.IO)
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: "*", // Be more restrictive in production
methods: ["GET", "POST"]
}
});
const PORT = process.env.PORT || 3000;
io.on('connection', (socket) => {
console.log('A user connected');
socket.on('disconnect', () => {
console.log('User disconnected');
});
});
// Endpoint for WordPress to send events to
app.post('/event-from-wp', express.json(), (req, res) => {
const eventData = req.body;
console.log('Received event from WordPress:', eventData);
// Broadcast the event to all connected clients
io.emit('custom_event', eventData);
res.json({ status: 'ok', message: 'Event broadcasted' });
});
server.listen(PORT, () => {
console.log(`WebSocket server listening on port ${PORT}`);
});
To run this, you’ll need Node.js installed. Install dependencies with npm install express socket.io and run with node server.js.
Bridging WordPress to the WebSocket Server
Now, we need to modify our WordPress handler to send a request to this Node.js server. We’ll use the WordPress HTTP API for this. Ensure your WebSocket server is accessible from your WordPress installation.
add_filter( 'rewrite_rules_array', 'myplugin_add_rewrite_rules' );
function myplugin_add_rewrite_rules( $rules ) {
$new_rules = array(
'myplugin/v1/event-trigger$' => 'index.php?myplugin_event=1',
);
return array_merge( $new_rules, $rules );
}
add_filter( 'query_vars', 'myplugin_add_query_vars' );
function myplugin_add_query_vars( $vars ) {
$vars[] = 'myplugin_event';
return $vars;
}
add_action( 'template_redirect', 'myplugin_handle_event_trigger' );
function myplugin_handle_event_trigger() {
if ( get_query_var( 'myplugin_event' ) ) {
// --- WebSocket Integration ---
$websocket_server_url = 'http://localhost:3000/event-from-wp'; // Replace with your WebSocket server endpoint
$event_payload = array(
'type' => 'user_action',
'data' => array(
'user_id' => get_current_user_id(),
'action' => 'triggered_event',
'timestamp' => current_time( 'mysql' )
)
);
$response = wp_remote_post( $websocket_server_url, array(
'method' => 'POST',
'timeout' => 5, // seconds
'body' => json_encode( $event_payload ),
'headers' => array(
'Content-Type' => 'application/json',
),
));
if ( is_wp_error( $response ) ) {
// Log error or handle it appropriately
error_log( 'WebSocket connection error: ' . $response->get_error_message() );
$status_code = 500;
$message = 'Failed to send event to WebSocket server.';
} else {
// Optionally check response from WebSocket server
$body = wp_remote_retrieve_body( $response );
$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code === 200 ) {
$message = 'Event received and forwarded to WebSocket server.';
} else {
error_log( 'WebSocket server returned error: ' . $status_code . ' - ' . $body );
$message = 'WebSocket server responded with an error.';
}
}
// --- End WebSocket Integration ---
status_header( $status_code );
header( 'Content-Type: application/json' );
echo json_encode( array( 'status' => $status_code === 200 ? 'success' : 'error', 'message' => $message ) );
exit;
}
}
// Flush rewrite rules on plugin activation/deactivation (as before)
register_activation_hook( __FILE__, 'myplugin_flush_rewrite_rules' );
function myplugin_flush_rewrite_rules() {
myplugin_add_rewrite_rules( array() );
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'myplugin_flush_rewrite_rules_deactivate' );
function myplugin_flush_rewrite_rules_deactivate() {
global $wp_rewrite;
$rules = $wp_rewrite->rules;
foreach( $rules as $rule => $match ) {
if ( strpos( $rule, 'myplugin/v1/event-trigger' ) !== false ) {
unset( $wp_rewrite->rules[$rule] );
}
}
flush_rewrite_rules();
}
Frontend Client Implementation (JavaScript)
On the frontend, JavaScript clients will connect to the WebSocket server and listen for the custom events. When an event is received, they can update the UI dynamically.
// frontend.js
document.addEventListener('DOMContentLoaded', () => {
// Replace with your WebSocket server URL
const socket = io('http://localhost:3000'); // Ensure this matches your server
socket.on('connect', () => {
console.log('Connected to WebSocket server');
});
socket.on('custom_event', (data) => {
console.log('Received custom event:', data);
// Example: Update a notification area on the page
const notificationArea = document.getElementById('notification-area');
if (notificationArea) {
const notification = document.createElement('div');
notification.textContent = `New Event: ${data.type} at ${data.data.timestamp}`;
notification.style.padding = '10px';
notification.style.borderBottom = '1px solid #eee';
notificationArea.prepend(notification); // Add to the top
}
});
socket.on('disconnect', () => {
console.log('Disconnected from WebSocket server');
});
// Example: Trigger an event from the frontend to WordPress endpoint
// This would typically be done via an AJAX call on a user action
const triggerButton = document.getElementById('trigger-event-button');
if (triggerButton) {
triggerButton.addEventListener('click', () => {
fetch('/wp-json/myplugin/v1/event-trigger', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'Frontend initiated event' }),
})
.then(response => response.json())
.then(data => console.log('Response from WordPress endpoint:', data))
.catch((error) => console.error('Error triggering event:', error));
});
}
});
This JavaScript code would be enqueued in your WordPress theme or plugin. The io() function connects to the Socket.IO server. The socket.on('custom_event', ...) listener waits for messages broadcasted from the server. The example also includes a button to demonstrate triggering the WordPress endpoint via AJAX, which in turn will trigger the WebSocket broadcast.
Production Considerations and Scalability
For production environments, several aspects require careful consideration:
- WebSocket Server Deployment: Running a Node.js server on the same infrastructure as WordPress might not be ideal. Consider dedicated microservices, containerization (Docker/Kubernetes), or managed WebSocket services (e.g., Pusher, Ably, AWS API Gateway WebSockets).
- Security: Implement authentication and authorization for both the WordPress endpoint and the WebSocket connections. For WebSockets, consider token-based authentication passed during the initial handshake.
- Scalability: The WebSocket server must be able to handle a large number of concurrent connections. Load balancing and horizontal scaling of the WebSocket server instances are crucial.
- Error Handling and Resilience: Implement robust error handling, retry mechanisms, and dead-letter queues for messages that cannot be delivered. Monitor WebSocket server health and performance.
- WordPress HTTP API: For high-traffic scenarios, the
wp_remote_postcalls from WordPress to the WebSocket server can become a bottleneck. Consider asynchronous processing using WP-Cron, message queues (RabbitMQ, Kafka), or background job processing. - CORS: Ensure Cross-Origin Resource Sharing (CORS) is correctly configured on the WebSocket server if your frontend is served from a different domain than your WordPress site.
- Rewrite Rule Management: For complex plugins with many custom endpoints, consider using a dedicated REST API plugin or framework that handles routing and endpoint registration more elegantly than manual rewrite rule manipulation.
By combining the WordPress Rewrite API for custom endpoints with a robust WebSocket infrastructure, you can build highly interactive and real-time features into your WordPress applications, catering to sophisticated enterprise requirements.