Skip to main content

Real-time API

The Codehooks.io real-time API enables applications and clients to publish and subscribe to data events.

API quick overview

State diagram

The following diagram shows the necessary dataflow between a client and a server.

realtime.createChannel(channel)

Create a real-time channel for clients listeners and and server publishers.

Parameters

  • channel: string - e.g. '/goals'

Returns: void.

Code example for creating a channel

index.js
import {app, realtime} from 'codehooks-js';

// create a real-time channel
realtime.createChannel('/goals');

/* more code here ... */

// bind to serverless runtime
export default app.init();

realtime.publishEvent(channel,data, [query])

Publish a data event on a channel to all or some (query) listeners.

Parameters

  • channel: string - e.g. '/goals'
  • data: object - Event object to publish
  • query: object - optional matching criteria for listeners to get the event

Returns: Promise: object

Code example for publish events

index.js
import {app, realtime} from 'codehooks-js';

// create a real-time channel
realtime.createChannel('/goals');

// broadcast event
async function someFunction() {
const listeners = {topic: "score"}; // send to all listeners subscribing to topic
const event = {"team A": 0, "team B": 1, "player": "Messi"};
const data = await realtime.publishEvent('/goals', event, listeners);
}

/* more logic here ...*/

// bind to serverless runtime
export default app.init();

realtime.createListener(channel, data)

Create a new listener (ID) that clients can use to subscribe to an EventSource channel.

Parameters

  • channel: string - Channel name, e.g. '/goals'
  • data: object - Listener topics of interest, e.g. {topic: "score"}

Returns: Promise: listenerData with a unique _id

Code example for creating a new listener

index.js
import {app, realtime} from 'codehooks-js';

// create a real-time channel
realtime.createChannel('/clock');

// client rest api to connect to the real time channel
app.post('/connect', async (req, res) => {
const listenerData = await realtime.createListener('/clock', req.body)
// return listener ID to client
res.json({listenerID: listenerData._id})
})

app.job('* * * * *', async (req, res) => {
const listeners = {topic: "tick"}; // send to all listeners subscribing to topic
const event = {"The time is": new Date()};
const data = await realtime.publishEvent('/clock', event, listeners);
})

// bind to serverless runtime
export default app.init();

realtime.getListener(channel, listenerID)

Get a listener object for a specific listener (ID) on a channel.

Parameters

  • channel: string - Channel name, e.g. '/goals'
  • listenerID: string - A valid _id for an existing listener

Returns: Promise: listenerData

realtime.getListeners(channel)

Get all listeners on a channel (channel).

Parameters

  • channel: string - Channel name, e.g. '/goals'

Returns: Promise: array of listenerData

realtime.removeListener(channel, listenerID)

Remove a listener (listenerID) from a channel (channel).

Parameters

  • channel: string - Channel name, e.g. '/goals'
  • listenerID: string - A valid _id for an existing listener

Returns: Promise: listenerData

Complete code example - Real-time chat

To better understand how the Codehooks.io real-time API works, check out the following code example. The complete source code, including install instructions, is avaliable at Github.

The code example is a simple web (plain vanilla ugly HTML) chat app for POST'ing messages to a REST API endpoint. The messages are then received on the client in real-time from the server. The server (Codehooks.io) uses the real-time API to subscribe and publish data to/from client requests.

real-time web chat

You can test out a live example here

HTML client page

/public/index.html
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Codehooks.io - Chat App</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/[email protected]/src/eventsource.js"></script>
</head>

<body>
<h1>Codehooks.io - Chat example</h1>

<div id="chat">
<label for="aliasInput">Chat Alias:</label>
<input type="text" id="aliasInput" placeholder="Enter your alias...">
<div id="status">Real-time status: <span id="statusIndicator" style="color: red;">Initializing</span></div>
<div id="messages" style="height: 150px; overflow: auto; border: 1px solid #ccc; margin-bottom: 10px;"></div>
<div id="inputContainer" style="display: flex; width: 100%;">
<input type="text" id="messageInput" placeholder="Type a message..."
style="flex-grow: 1; margin-right: 10px;">
<button id="sendButton">Send</button>
</div>
</div>
<script src="chat.js"></script>
</body>

</html>

HTML client JavaScript

/public/chat.js
const API_TOKEN = '0a2249d4-5f10-489c-8229-1f060ad1e0f6';
let listenerID = null;

document.addEventListener('DOMContentLoaded', function() {
const aliasInput = document.getElementById('aliasInput');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');

// Function to send a message to the server
function sendMessage(message) {
fetch('/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-apikey': API_TOKEN
},
body: JSON.stringify({ message: message, listenerID, alias: aliasInput.value.trim() }),
})
.then(response => response.json())
.then(data => {
console.log('Message sent:', data);
messageInput.value = ''; // Clear input after sending
messageInput.focus(); // Keep focus on input field
})
.catch((error) => {
console.error('Error:', error);
});
}

sendButton.addEventListener('click', function() {
if (messageInput.value) {
sendMessage(messageInput.value);
}
});

messageInput.addEventListener('keydown', function(event) {
// Check if Enter key is pressed without Shift key
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault(); // Prevent default to avoid line breaks or form submission
if (messageInput.value) {
sendMessage(messageInput.value);
}
}
});
// init real-time connection
startListener();
});

function addMessage(message) {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.textContent = message;
messagesDiv.appendChild(messageElement); // Adds the message at the bottom
// Auto-scroll to the bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}


// connect to realtime SSE
async function startListener() {
// setup the real time stuff
const statusIndicator = document.getElementById('statusIndicator');

const interests = {}; // everything
var requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-apikey': API_TOKEN
},
body: JSON.stringify(interests)
};
// get a real time listenerID
console.log('Connect request', requestOptions)
const response = await fetch('/connect', requestOptions);
const result = await response.json();
listenerID = result.listenerID;
console.log('GOT clientID', result)

// connect to reatime channel
let eventSource = new EventSourcePolyfill(`/chat/${result.listenerID}`, {
headers: {
'x-apikey': API_TOKEN
}
});
statusIndicator.textContent = 'Connected';
statusIndicator.style.color = 'green';

// incoming message event
eventSource.onmessage = function (event) {
console.log("Event", event.data)
const result = JSON.parse(event.data);
addMessage(result.message)
};

// here we go
eventSource.onopen = function(event) {
// Connection is open
statusIndicator.textContent = 'Live data ready';
statusIndicator.style.color = 'green';
};
// oops, reconnecting if possible
eventSource.onerror = function(event) {
console.log("Error", event);
// An error occurred or the connection was closed
if (eventSource.readyState == EventSource.CLOSED) {
console.log("Connection was closed.");
}
statusIndicator.textContent = 'No connection';
statusIndicator.style.color = 'red';
};

return result;
}

Codehooks.io Server

See install and deploy docs at Github.

index.js
// Codehooks.io - Chat example

import {app, realtime} from 'codehooks-js';

// create a real-time channel
realtime.createChannel('/chat');

// client connects for a new real-time listener ID
app.post('/connect', async (req, res) => {
const listenerData = await realtime.createListener('/chat')
// return listener ID to client
res.json({listenerID: listenerData._id})
})

// client post a new message
app.post('/messages', async (req, res) => {
console.log('Message in', req.body)
const {message, listenerID, alias} = req.body;
const data = await realtime.publishEvent('/chat', {"message": `${alias || 'Anonymous'}: ${message}`});
res.end(data)
})

// annoying message from a cron job every second minute ;)
app.job('*/2 * * * *', async (_, job) => {
const message = `Hello from cron job at ${new Date().toISOString()}`;
const data = await realtime.publishEvent('/chat', {"message": `cron: ${message}`});
job.end()
})

app.static({route:'/public', directory: '/public'})

// bind to serverless runtime
export default app.init();