Skip to content

Commit

Permalink
add v1.3 blog post
Browse files Browse the repository at this point in the history
  • Loading branch information
bensie committed Oct 14, 2024
1 parent eab5bec commit b91ca5c
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 16 deletions.
153 changes: 153 additions & 0 deletions blog/2024-10-14-new-in-v1.3.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
slug: message-storage-and-client-http-api
title: Message storage and client HTTP API now available
authors: [james]
tags: []
---

I'm excited to share the latest Hotsock release, which supports **storing messages for any duration you specify** alongside a **client HTTP API for querying message history** in your channels.

{/* truncate */}

### Storing messages

In the just-released Hotsock v1.3, you can now choose to store published messages on a per-message basis and your connected clients can retrieve the history of messages on the channels where they are subscribed.

Take a chat app as an example, where you have the actual chat content between your users as well as typing indicators published as messages to a channel. You'd like to save all the chat messages, but it would be wasteful to store all the typing indicator messages.

When publishing server-side messages, you can now (optionally) specify a [`store`](/docs/server-api/publish-messages/#message-format.store) attribute, where the value is the number of seconds to retain the message. **Whether you want to store a message for 30 seconds, 30 days, or 30 years, it's up to you!**

Here's an example payload that will keep this message for 30 days (60 seconds \* 60 minutes \* 24 hours \* 30 days = 2592000) before it expires and is automatically deleted.

```json
{
"channel": "my-channel",
"event": "my-event",
// highlight-next-line
"store": 2592000
}
```

Similarly for client-initiated messages, you can now specify a [`store`](/docs/connections/claims/#channels.messages.store) claim, where any message sent from a channel subscription will be stored according to the number of seconds specified in that claim.

Here are example claims for the chat app example above, where typing indicators are not stored, but chat messages are stored for about 1 year (365 days \* 24 hours \* 60 minutes \* 60 seconds) or 31,536,000 seconds.

```json
{
"channels": {
"my-channel": {
"messages": {
"is-typing": {
"publish": true
},
"chat": {
"publish": true,
// highlight-next-line
"store": 31536000
}
}
}
}
}
```

### Query channel message history

Using the new [client HTTP API](/docs/connections/client-http-api/), you can make HTTP requests from your client applications to fetch message history of stored messages, authorized by using the `connectionId` and `connectionSecret` returned in the `hotsock.connected` WebSocket message received when a connection is established.

#### Request

```javascript
fetch(
"https://r6zcm2.lambda-url.us-east-1.on.aws/connection/listMessages?connectionId=fjlb_eHLIAMCKRg%3d&connectionSecret=SZy32Etv0KIbe4Jod6KH",
{
method: "POST",
body: JSON.stringify({ channel: "my-channel" }),
}
)
```

#### Response

```json
{
"messages": [
{
"id": "01JA3S0TNEC2QBYMZRBMCEXGNW",
"event": "my-event",
"channel": "my-channel",
"data": "👋 Hello!",
"meta": {
"uid": null,
"umd": null
}
}
]
}
```

Additional details and options for this endpoint are available [in the docs](/docs/connections/client-http-api/#connection/listMessages).

### Expand visibility with the `historyStart` claim

By default, connections can view message history **for channels where they are currently subscribed, but are limited to viewing messages that were published and stored during the lifetime of the current WebSocket connection's subscription.**

On its face, this may not seem useful - if I can only fetch messages that I've already received while subscribed to a channel, why would I want to load another copy of those same messages? To keep existing behavior unchanged and to not unexpectedly grant subscribers access to old channel messages that they should not be able to see, this is the _default_ behavior.

The [`historyStart`](/docs/connections/claims/#channels.historyStart) claim allows you to modify this behavior, expanding the timeframe of retrievable messages on a channel. This claim accepts a Unix timestamp, which represents the timestamp of the oldest visible message.

The following grants authorization to retrieve all stored messages since July 1, 2024 00:00:00 UTC (`1719792000`) using the `listMessages` API.

```json
{
"channels": {
"my-channel": {
// highlight-next-line
"historyStart": 1719792000,
"messages": {
"is-typing": {
"publish": true
},
"chat": {
"publish": true,
"store": 31536000
}
}
}
}
}
```

With this claim in force, this client can obtain the history of the channel back to July 1, 2024, but cannot view anything before that.

### Publish client messages with HTTP

In addition to listing messages via the HTTP API, **you can also publish messages to subscribed channels with a standard `POST` request**. HTTP-based client message publishing uses the same [`publish`](/docs/connections/claims/#channels.messages.publish) claim as WebSocket-based publishing, but adds the full request/response cycle of an HTTP request and making it easier to keep track of success/failure of message delivery.

#### Request

```javascript
fetch(
"https://r6zcm2.lambda-url.us-east-1.on.aws/connection/publishMessage?connectionId=fjlb_eHLIAMCKRg%3d&connectionSecret=SZy32Etv0KIbe4Jod6KH",
{
method: "POST",
body: JSON.stringify({ channel: "my-channel", event: "my-event" }),
}
)
```

#### Response

```json
{
"id": null,
"event": "my-event",
"channel": "my-channel"
}
```

Additional details and options for this endpoint are available [in the docs](/docs/connections/client-http-api/#connection/publishMessage).

### Wrapping up

Existing installations with auto-update enabled are already running v1.3 and have access to these features today. Other installations can be [manually updated](/docs/installation/updates/#manually-update-installation) at any time.
34 changes: 18 additions & 16 deletions docs/connections/client-http-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,43 +45,43 @@ https://r6zcm2.lambda-url.us-east-1.on.aws/connection/publishMessage?connectionI
Notice the **%3d** in `JCnTZd_KoAMCF-A%3d`, replacing the **=** in the original connection ID `JCnTZd_KoAMCF-A=`. Connection IDs typically have an equals sign as their suffix, so this value must be URL encoded for the URL to be parsed correctly.
:::

## `connection/listMessages` {#listMessages}
## `connection/listMessages` {#connection/listMessages}

List the message history for a channel. By default, a connection can only list messages for channels that they are subscribed to, limited to messages that were published during the timeframe of the active subscription on the current WebSocket connection. If you subscribed to a channel 2 minutes ago, you'll only be able to list message history on that channel for the past 2 minutes.

When issuing a connect or subscribe token, specify the [`historyStart`](./claims.mdx#channels.historyStart) claim to expand visibility to message history beyond the lifetime of the active subscription.

This endpoint will return up to 100 messages or up to 1MB of data, whichever comes first. Fetch subsequent pages using `before` or `after`, depending on your use case.

#### `after` {#listMessages.after}
#### `after` {#connection/listMessages.after}

String (optional) - Load messages after (newer than) the message with the ID specified here. Has no effect if `reverse` is `true`.

Message IDs are [ULIDs](https://github.com/ulid/spec), making them time-sortable by their value. If you receive a response containing 100 messages and have `reverse` set to `false` (default), you can get the next 100 messages by setting `after` to the last message ID you received.

Example: `01JA3HVNF6E89HDT1ABRFWJ8C8`

#### `before` {#listMessages.before}
#### `before` {#connection/listMessages.before}

String (optional) - Load messages before (older than) the message with the ID specified here. Has no effect if `reverse` is `false` (which is the default).

Message IDs are [ULIDs](https://github.com/ulid/spec), making them time-sortable by their value. If you receive a response containing 100 messages and have `reverse` set to `true`, you can get the next 100 older messages by setting `before` to the last message ID you received.

Example: `01JA3HVNF6E89HDT1ABRFWJ8C8`

#### `channel` {#listMessages.channel}
#### `channel` {#connection/listMessages.channel}

String (required) - The name of the channel to query message history.

#### `reverse` {#listMessages.reverse}
#### `reverse` {#connection/listMessages.reverse}

Boolean (optional) - If set to `true`, lists messages from newest to oldest. Default is `false`.

### Example
### Example {#connection/listMessages--example}

The following will load up to 100 of the most recent messages from the `my-channel` channel.

#### Request
#### Request {#connection/listMessages--example-request}

```javascript
fetch(
Expand All @@ -93,7 +93,7 @@ fetch(
)
```

#### Response
#### Response {#connection/listMessages--example-response}

```json
{
Expand Down Expand Up @@ -152,7 +152,7 @@ fetch(
}
```

## `connection/publishMessage` {#publishMessage}
## `connection/publishMessage` {#connection/publishMessage}

Publish a message to a channel. For `publishMessage` requests to succeed, the connection must already be subscribed to the channel and must have [`publish`](./claims.mdx#channels.messages.publish) permissions granted to the client.

Expand All @@ -162,27 +162,27 @@ You might wonder when it's appropriate to send a message [via the WebSocket](htt
HTTP API calls give you the control of a full HTTP request/response. If you receive a `202 Accepted` response, you can be certain that the message was received by the server.
:::

#### `channel`
#### `channel` {#connection/publishMessage.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`
#### `data` {#connection/publishMessage.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.

#### `eagerIdGeneration`
#### `eagerIdGeneration` {#connection/publishMessage.eagerIdGeneration}

`Boolean` (optional) - If you need to store a copy of the message ID that is published to the channel for follow-up requests, set this to `true`. Default is `false`. [Learn more](../server-api/publish-messages.mdx#message-format.eagerIdGeneration)

#### `event`
#### `event` {#connection/publishMessage.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.

### Example
### Example {#connection/publishMessage--example}

The following will publish a message to the `my-channel` channel.

#### Request
#### Request {#connection/publishMessage--example-request}

```javascript
fetch(
Expand All @@ -198,7 +198,9 @@ fetch(
)
```

#### Response
#### Response {#connection/publishMessage--example-response}

Expect a `202 Accepted` status code for successful publishes.

```json
{
Expand Down

0 comments on commit b91ca5c

Please sign in to comment.