HPKV WebSocket API
WebSocket API
The WebSocket API provides a persistent connection for high-performance operations with HPKV. It's designed for applications that need to minimize latency and reduce overhead from multiple HTTP requests.
Connection
Connect to the WebSocket endpoint using your base URL and API key. You can provide the API key in two ways:
const ws = new WebSocket('wss://YOUR_BASE_URL/ws?apiKey=YOUR_API_KEY');
const ws = new WebSocket('wss://YOUR_BASE_URL/ws?apiKey=YOUR_API_KEY');
ws.onopen = () => {
console.log('Connected to HPKV');
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log('Received:', response);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected from HPKV');
};
Message Format
All messages are JSON objects with the following structure:
{
"op": number, // Operation code
"key": string, // The key to operate on
"value": string, // The value (for insert/update operations)
"messageId": number // Optional: ID to match responses with requests
}
Operations
Operation Codes (op):
1: Get - Retrieve a record by key
2: Insert/Update - Create a new record or replace existing
3: JSON Patch/Append - Append to an existing record or apply JSON patch if both values are valid JSON
4: Delete - Remove a record by key
5: Range - Retrieve multiple records in a key range
6: Atomic Increment/Decrement
1. Insert/Update a Record
ws.send(JSON.stringify({
op: 2,
key: "user:123",
value: "John Doe",
messageId: 1
}));
2. Get a Record
ws.send(JSON.stringify({
op: 1,
key: "user:123",
messageId: 2
}));
3. JSON Patch/Append to a Record
If the record "user:123" has value "Hello ", appending "World!" will result in "Hello World!"
ws.send(JSON.stringify({
op: 3,
key: "user:123",
value: "World!", // Will be appended to existing value
messageId: 3
}));
JSON Atomic Patching
When using the Update operation (op 3), if both the existing stored value and the new value are valid JSON objects, HPKV will automatically perform a JSON patch operation instead of simple appending. This merges the JSON objects, updating existing fields and adding new ones.
For example, if the record "user:123" has value {"name":"John","age":25}
and you send an update with {"age":30,"city":"New York"}
, the resulting value will be {"name":"John","age":30,"city":"New York"}
.
If either value is not valid JSON, the system falls back to the standard append behavior.
4. Delete a Record
ws.send(JSON.stringify({
op: 4,
key: "user:123",
messageId: 4
}));
5. Range Query
Retrieve multiple records within a key range.
{
"op": 5, // Operation code
"key": "string", // startKey (inclusive)
"endKey": "string", // endKey (inclusive)
"limit": number, // Optional: max records to return (default: 100, max: 1000)
"messageId": number // Optional: ID to match responses with requests
}
ws.send(JSON.stringify({
op: 5,
key: "user:100", // startKey
endKey: "user:200", // endKey
limit: 50, // Optional: max records to return
messageId: 5
}));
Note
Range queries are perfect for retrieving collections of related data. For example, you can fetch all products in a category with key="product:category1:"
and endKey="product:category1:~"
.
The tilde (~) character has a high ASCII value, making it useful as an end marker for prefixed keys.
6. Atomic Increment/Decrement
Atomically increment or decrement a numeric value stored at the specified key.
{
"op": 6,
"key": "string",
"value": number, // Positive to increment, negative to decrement
"timestamp": number, // Optional: milliseconds since epoch
"messageId": number // Optional: ID to track this request
}
ws.send(JSON.stringify({
op: 6,
key: "counter:123",
value: 1, // Use negative value to decrement
messageId: 6
}));
Response:
{
"success": true,
"message": "Record incremented/decremented successfully",
"newValue": 42, // The new value after increment/decrement
"code": 200,
"messageId": 6
}
Note
Atomic operations are useful for counters, rate limiters, and other scenarios where you need to ensure consistency without race conditions. The system will automatically create the key if it doesn't exist in case of an increment.
Key Monitoring (Pub-Sub)
HPKV provides a bi-directional WebSocket feature that enables real-time monitoring of key changes. This pub-sub system automatically notifies clients when monitored keys are modified through any interface (REST or WebSocket).
1. Generate a Monitoring Token
First, generate a WebSocket token with the keys you want to monitor using the REST API:
# Generate a token with keys to monitor
curl -X POST "https://YOUR_BASE_URL/token/websocket" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscribeKeys": ["user:123", "counter:456", "product:789"],
"accessPattern": "^(user|product):[0-9]+"
}'
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Access Pattern Control
The optional accessPattern
parameter allows you to restrict which keys the token can perform operations on (get, insert, update, delete, range, atomic increment/decrement).
This parameter accepts a valid regex pattern string that will be used to validate keys for all operations except subscription notifications.
For example, the pattern ^(user|product):[0-9]+
would only allow operations on keys that start with "user:"
or "product:"
followed by numbers; or the pattern $^
can be used to restrict all operations for the token, except for subscription notifications.
Note
The access pattern only restricts which keys the token can perform operations on. It does not affect the subscription keys specified in the subscribeKeys
array. You will continue to receive notifications for all subscribed keys regardless of the access pattern.
Regex Pattern Security Restrictions
For security reasons, certain regex features are not allowed in access patterns:
Disallowed regex features include:
- Backreferences (
\1
,\2
, etc.) - Lookaheads and lookbehinds (
(?=...)
,(?<=...)
, etc.) - Nested repetition quantifiers (
(a+)+
) - Possessive quantifiers (
a++
,a*+
)
Allowed regex features include:
- Basic character matching (
a-z
,0-9
, etc.) - Simple quantifiers (
*
,+
,?
) not nested - Character classes (
[abc]
,[^abc]
) - Alternation (
a|b
) - Anchors (
^
,$
) - Groups for organization (not for backreferences)
2. Connect with the Token
Connect to the WebSocket endpoint using the generated token:
const ws = new WebSocket('wss://YOUR_BASE_URL/ws?token=YOUR_TOKEN');
ws.onopen = () => {
console.log('Connected to HPKV monitoring service');
};
ws.onmessage = (event) => {
const notification = JSON.parse(event.data);
if (notification.type === 'notification') {
console.log('Key change notification:', notification);
// Handle the notification based on key and value
}
};
3. Notification Format
When a monitored key changes, you'll receive a notification with the following format:
{
"type": "notification",
"key": "user:123",
"value": "John Doe", // The new value, will be null if key was deleted
"timestamp": 1628701234567
}
4. Handling Notifications
Here's how to handle the notifications in various programming languages:
// Process notifications
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'notification') {
const { key, value, timestamp } = data;
if (value === null) {
console.log(`Key ${key} was deleted at ${new Date(timestamp)}`);
// Handle deletion (e.g., remove from UI)
removeItemFromUI(key);
} else {
console.log(`Key ${key} was updated to ${value} at ${new Date(timestamp)}`);
// Handle update (e.g., update UI with new value)
updateItemInUI(key, value);
}
}
};
Note
The key monitoring feature follows a pub-sub pattern. Any change to monitored keys will trigger notifications to all connected clients that are subscribed to those keys.
Each key must be specified exactly as it will appear in the database. Only exact key matches will trigger notifications.
Response Format
Responses are JSON objects with the following structure:
{
"code": number, // HTTP-style status code
"messageId": number, // Matches the request messageId if provided
"key": string, // Present in successful GET responses
"value": string, // Present in successful GET responses
"error": string, // Present when an error occurs
"success": boolean // Present in successful write operations
}
Success Response (Range Query):
{
"records": [
{
"key": "user:100",
"value": "Alice"
},
{
"key": "user:101",
"value": "Bob"
},
// ... more records
],
"count": 50,
"truncated": false,
"code": 200,
"messageId": 5
}
Error Handling
Common error codes in responses:
400
: Invalid request format403
: Permission denied404
: Record not found429
: Rate limit exceeded500
: Internal server error
Live Demo
We've created a simple yet functional Todo application to demonstrate the WebSocket API in action. The demo is a frontend-only application that uses HPKV as its backend storage through WebSocket connections, showcasing real-time updates and persistent storage capabilities. Multiple users connecting with the same API key can collaborate in real-time, seeing each other's changes instantly as they add, complete, or remove todos.

Note
You'll need a valid API key to try the demo.
Not signed up yet? Get started with HPKV today and experience lightning-fast, real-time data storage for your applications. Sign up now →
Best Practices
- Use messageId to match responses with requests
- Implement reconnection logic with exponential backoff
- Handle connection errors and implement proper error recovery
- Monitor the connection state and implement heartbeat if needed
- Consider using a WebSocket client library that handles reconnection