Publish Messages 📝
Sending server-side messages to subscribers on Hotsock channels is the most common way to enable real-time features in your applications. Hotsock allows you to publish messages from your backend by invoking a Lambda function or by making an authenticated HTTP request. Connected clients can also send client-initiated messages.
Publish with Lambda
This is the recommended method for publishing events from your backend. If you have the AWS SDK available in your backend application or are able to add it as a dependency, take this route.
All AWS SDKs across all available languages can invoke a Lambda function securely, enforcing least-privilege access using IAM roles with temporary credentials that are refreshed automatically by your EC2 instance, ECS task, or Lambda function.
Using the language of your choice, invoke the function referenced in the PublishFunctionArn
output to publish a message. Copy the Arn from the stack output to the function name argument in your invoke call (either the full Arn or just the function name are supported).
- Go
- JS
- Ruby
- Python
- C#
- Java
- PHP
- AWS CLI
lambdaClient.Invoke(context.TODO(), &lambda.InvokeInput{
FunctionName: aws.String("Hotsock-PublishFunction-AAABBBCCCDDD"),
Payload: []byte(`{"channel": "my-channel", "event": "my-event", "data": "👋"}`),
})
View the full documentation in the API reference.
lambdaClient.send(
new InvokeCommand({
FunctionName: "Hotsock-PublishFunction-AAABBBCCCDDD",
Payload: JSON.stringify({
channel: "my-channel",
event: "my-event",
data: "👋",
}),
})
)
View the full documentation in the API reference
There's an official Hotsock Ruby Gem if you prefer.
lambda_client.invoke({
function_name: 'Hotsock-PublishFunction-AAABBBCCCDDD',
payload: {
channel: 'my-channel',
event: 'my-event',
data: '👋'
}.to_json
})
View the full documentation in the API reference
lambda_client.invoke(
FunctionName='Hotsock-PublishFunction-AAABBBCCCDDD',
Payload=json.dumps({
'channel': 'my-channel',
'event': 'my-event',
'data': '👋'
})
)
View the full documentation in the API reference
await lambdaClient.Invoke(new InvokeRequest
{
FunctionName = "Hotsock-PublishFunction-AAABBBCCCDDD",
Payload = "{\"channel\":\"my-channel\",\"event\":\"my-event\",\"data\":\"👋\"}"
});
View the full documentation in the API reference
lambdaClient.invoke(new InvokeRequest()
.withFunctionName("Hotsock-PublishFunction-AAABBBCCCDDD")
.withPayload("{\"channel\":\"my-channel\",\"event\":\"my-event\",\"data\":\"👋\"}"));
View the full documentation in the API reference.
$lambdaClient->invoke([
'FunctionName' => 'Hotsock-PublishFunction-AAABBBCCCDDD',
'Payload' => json_encode([
'channel' => 'my-channel',
'event' => 'my-event',
'data' => '👋'
]),
]);
View the full documentation in the API reference
aws lambda invoke \
--function-name Hotsock-PublishFunction-AAABBBCCCDDD \
--payload '{"channel":"my-channel", "event":"my-event", "data":"👋"}' \
--cli-binary-format raw-in-base64-out \
/dev/stdout
View the full documentation in the API reference
For more complete examples - package imports, configuring the Lambda client, etc., reference the documentation for your preferred SDK.
Permissions to invoke Lambda
In each language-specific example below, lambdaClient
or lambda_client
is assumed to be a client configured to run as an IAM role or user that has permission to invoke your installation's publish function. Copy your function Arn from your installation's PublishFunctionArn
output - the permissions assigned to your IAM role or user should look something like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["lambda:InvokeFunction"],
"Effect": "Allow",
"Resource": [
"arn:aws:lambda:us-east-1:111111111111:function:Hotsock-PublishFunction-AAABBBCCCDDD"
]
}
]
}
Publish with HTTP URL
There are some downsides to using this publish method. It is and will continue to be fully supported, but the Lambda approach is more secure.
- The API keys that can be used for all requests are static and must be used by all of your backend clients. Whether your backend publishers store a copy of this key or request its current value from Parameter Store periodically, having a shared key poses a security risk if the key leaks.
- Hotsock grabs the current API key values from Parameter Store during each Lambda cold-start. This adds some (though minimal) cold-start latency because of the inline call to another AWS service.
- API keys can be used to publish messages from anywhere on the Internet by anyone who has a key. Not ideal from a usage auditing perspective and often tough to keep track of who has the key(s).
- Rotating the key is a manual process that you need to remember to do periodically.
Access your API keys
A pair of API keys were created alongside your installation. Their values are available by following the URLs in HttpApiSecretKey1ConsoleUrl and HttpApiSecretKey2ConsoleUrl.
You can use either key for HTTP-based message publishing. There are two keys to allow for key rotation without downtime - you can change the value of one while continuing to use the other.
Get your publish URL
Your HTTP URL is displayed in the PublishHttpApiUrl stack output of your installation.
Make a publish request
Using any HTTP client in your code, make a POST
request to your publish URL with the Authentication
header set to Bearer YOUR_API_KEY
and a JSON body containing at least an event
and channel
to publish to. Refer to the message format guide below for other available fields.
Here's an example with curl
.
curl -X "POST" "https://e4zdy87vg9byo4ugvldt3rmn440lflwe.lambda-url.us-east-1.on.aws/publish" \
-H 'Authorization: Bearer JAnzQFqRXsBgV0kKvd2DYhJMk77IhL8j9J2sLi5b' \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"channel": "my-channel",
"event": "my-event"
}'
You can adapt this for use with any HTTP client library in any programming language.
Message format
The shape of a message is the same whether you're invoking Lambda directly or using the HTTP API. At a minimum all messages must contain a channel
and an event
. Additional supported attributes are listed below.
{
"channel": "my-channel",
"event": "my-event"
}
channel
String
(required) - The name of the channel where this message will be published. This can be any string up to 128 characters, but must not contain any asterisk (*
), number sign (#
), comma (,
), or whitespace/newline characters.
data
JSON
(optional) - Up to 32KiB of custom data specific to your application. This can be anything that is valid JSON - object, array, string, number, boolean, or null
. Binary data must be Base64-encoded to a string.
deduplicationId
String
(optional) - If supplied as a non-empty string and if multiple messages with the same deduplicationId
value are received within 5 minutes of each other, only the first message will be published. Additional copies will be dropped.
If you're publishing from a source that retries upon failure, set this to a value that is consistent across all attempts to publish that message. This ensures that if Hotsock receives the message but a networking issue prevents your application from receiving the success response, retry attempts will not publish duplicate messages.
Providing a deduplicationId
while also enabling eagerIdGeneration
is not recommended. See below for details.
eagerIdGeneration
Boolean
(optional) - If you need to store a backend copy of the message ID that is published to the channel for follow-up requests, set this to true
. Default is false
.
Enabling this can ever-so-slightly affect message ordering. When this is option is disabled, messages receive a monotonically increasing ID at the time they are processed, ensuring that messages are always processed and identifiable sequentially within a channel. When this option is enabled (true
), messages receive an ID at publish time prior to being enqueued for processing, which is returned to the publisher. In scenarios where multiple messages are published within a few milliseconds, actual processing order is indeterminate.
Enabling eagerIdGeneration
while also providing a deduplicationId
is not recommended.
Consider the following scenario:
- A publish request made with
eagerIdGeneration
enabled anddeduplicationId
set to "ECHO". Hotsock received the message and the message ID in its response is01H62H35Q7JJBDB4EE5XA44CHV
. - Due to unstable network conditions, the requestor did not receive this response.
- Since the requestor saw the publish as a failure, they re-publish the message with the same
deduplicationId
set to "ECHO". This time the response message ID is01H62H8HYRCY1F6ZS85BX24B9R
but since the initial message was already received, this follow-up message will never be processed and no message with the second01H62H8HYRCY1F6ZS85BX24B9R
ID will ever be processed or sent to channel subscribers.
Deduplication is handled internally by SQS. Since messages are accepted by SQS successfully even when they are duplicates, there's no way for Hotsock to indicate in the message publishing response that a message is a duplicate.
emitPubSubEvent
Boolean
(optional) - Whether or not this message will trigger a backend pub/sub event to SNS/EventBridge. This setting has no effect if both SNS and EventBridge events are disabled globally. Default is false
.
event
String
(required) - The name of the event to publish to the channel. This can be any string up to 128 characters, but must not begin with hotsock.
and must not contain any asterisk (*
) characters.
store
Integer
(optional) - The message retention duration in seconds. 0
is the default if unspecified, which does not save message data. -1
keeps the message forever. The highest number you can specify here is 3155695200
, roughly 100 years. For example, setting this to 2592000
(60 seconds * 60 minutes * 24 hours * 30 days) would store the message for 30 days.