Fetch Messages from Storage
The Storage service allows you to persist all messages as they're published. The primary use case of Storage is to allow a device to retrieve messages that were published while that device was offline, but there are many other use cases as well. The Storage message retention can be configured for several different limited periods or unlimited.
When a message is published, it's stored using the channel name and the message's publish timetoken which can be used to retrieve, delete or annotate.
Message Retention
To retain a message, you'll need to enable the Storage feature for the given API Key in the Admin Portal. You may also want to configure the duration of the retention and whether you want to retain Presence Events. Once configured, the Storage feature is enabled for all the channels that belong to the API Key.
Can't Change Message Retention
Once a message is stored, you can't change its retention. Changes to Storage retention only apply to messages published after the change has been made.
Retrieving Messages
Retrieving messages from Storage is accomplished using the History API. You can retrieve up to 25 messages per channel from up to 500 channels in a single request.
Even though the History API returns all message types, the value of the returned messageType
parameter indicates what type a particular message is.
Message Type | Description |
---|---|
0 | Regular message |
1 | Signal |
2 | Objects event |
3 | Message Actions event |
4 | File message |
You can provide start and end timetokens to get messages from a specific time range. Most use cases require retrieving only the most recent messages since a given timetoken (missed messages since last online). For this, you need only provide the end parameter with a timetoken of the last received message. The following example will retrieve the last 25 (default/max) messages on the two channels.
pubnub.fetchMessages(
{
channels: ["chats.room1", "chats.room2"],
end: '15343325004275466',
count: 25 // default/max is 25
},
function(status, response) {
console.log(status, response);
}
);
pubnub.fetchMessageHistory(
for: ["chats.room1", "chats.room2"],
end: "15343325004275466"
) { result in
switch result {
case let .success(response):
print("Successful History Fetch Response: \(response)")
case let .failure(error):
print("Failed History Fetch Response: \(error.localizedDescription)")
}
}
self.pubnub.history()
.channels(@[@"chats.room1", @"chats.room2"])
.end(15343325004275466).limit(25)
.performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) {
// handle returned messages in result
});
pubnub.fetchMessages()
.channels(Arrays.asList("my_channel"))
.maximumPerChannel(25)
.end(15343325004275466L)
.async(new PNCallback<PNFetchMessagesResult>() {
@Override
public void onResponse(PNFetchMessagesResult result, PNStatus status) {
if (!status.isError()) {
Map<String, List<PNFetchMessageItem>> channels = result.getChannels();
for (PNFetchMessageItem messageItem : channels.get("my_channel")) {
System.out.println(messageItem.getMessage());
System.out.println(messageItem.getMeta());
System.out.println(messageItem.getTimetoken());
for (String type : actions.keySet()) {
System.out.println("Action type: " + type);
for (String value : actions.get(type).keySet()) {
System.out.println("Action value: " + value);
for (PNFetchMessageItem.Action action : actions.get(type).get(value)) {
System.out.println("Action timetoken: " + action.getActionTimetoken());
System.out.println("Action publisher: " + action.getUuid());
}
}
}
}
}
else {
status.getErrorData().getThrowable().printStackTrace();
}
}
});
pubnub.FetchHistory()
.Channels(new string[] { "my_channel" })
.MaximumPerChannel(25)
.End(15343325004275466)
.Execute(new PNFetchHistoryResultExt((result, status) => {
// handle returned messages in result
}));
envelope = pubnub.fetch_messages()\
.channels(["chats.room1", "chats.room2"])\
.count(25)\
.end(15343325004275466)\
.sync()
If more than 25 messages are needed, you can use the timetokens of the returned message to go further back in time to retrieve more messages. In other words, you can page through a channel's timeline recursively until you all the messages you require.
Parameters Used | Behavior |
---|---|
start | Retrieves messages before the start timetoken, excluding any message at that timetoken |
end | Retrieves messages after to the end timetoken, including any message at that timetoken |
start & end | Retrieves messages between the start and end timetokens, excluding any message at the start timetoken and including any message at the end timetoken |
Receiving Messages and Signals
When a message, or signal is received by the client, it triggers a message
or signal
event, respectively. Refer to Add Listener in the Application Setup section to learn how to act upon these events.
How Messages are Fetched
The following details explain how messages are retrieved from Storage based on the start, end, and reverse parameters.
Scenario 1
Scenario 1 retrieves messages starting with the message stored before the start timetoken parameter value and continues until it has 25 messages or hits that oldest message (whichever comes first).
Parameter | Value |
---|---|
count | 25 |
start | value provided |
end | value not provided |
Channel Timeline
oldest-message --------------- start-timetoken --------------- newest-message
[ <--------]
Scenario 2
Parameter | Value |
---|---|
count | 25 |
start | value not provided |
end | value provided |
Channel Timeline
oldest-message --------------- end-timetoken --------------- newest-message
[ <---------------------]
Scenario 3
Parameter | Value |
---|---|
count | 25 |
start | value provided |
end | value provided |
Channel Timeline
oldest-message ----- end-timetoken ----------------- start-timetoken ----- newest-message
[ <----------------------]
Getting Message Counts
Rather than retrieving lots of messages from hundreds of channels, you could just get the number of missed messages and retrieve the messages later. For example, you can display the unread message count on channels that the client hasn't yet visited and retrieve those messages either when the client actually visits the channel or a lower priority background thread.
The Message Count API only returns the number of messages sent for each channel greater than or equal to the provided timetoken. You can not specify a time range because it's only intended to give the number of messages since a given timetoken. Optionally, you can specify a different timetoken per channel or one timetoken to be applied to all channels.
pubnub.messageCounts({
channels: ["chats.room1", "chats.room2"],
channelTimetokens: ['15518041524300251']
}).then((response) => {
console.log(response)
}).catch((error) => {
// handle error
}
);
pubnub.messageCounts(
channels: ["chats.room1", "chats.room2"]),
timetoken: 15495750401727535
) { result in
switch result {
case let .success(response):
print("Successful Message Count Response: \(response)")
case let .failure(error):
print("Failed Message Count Response: \(error.localizedDescription)")
}
}
self.client.messageCounts().channels(@[@"chats.room1", @"chats.room2"])
.timetokens(@[@(15495750401727535)])
.performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) {
if (!status.isError) {
// Client state retrieved number of messages for channels.
}
else {
// handler error condition
}
});
pubnub.messageCounts()
.channels(Arrays.asList("chats.room1", "chats.room2"))
.channelsTimetoken(Arrays.asList(15495750401727535L))
.async(new PNCallback<PNMessageCountResult>() {
@Override
public void onResponse(PNMessageCountResult result, PNStatus status) {
if (!status.isError()) {
for (Map.Entry<String, Long> entry : result.getChannels().entrySet()) {
entry.getKey(); // the channel name
entry.getValue(); // number of messages for that channel
}
}
else {
status.getErrorData().getThrowable().printStackTrace();
}
}
});
pubnub.MessageCounts()
.Channels(new string[] { "chats.room1", "chats.room2" })
.ChannelsTimetoken(new long[] { 15495750401727535 })
.Execute(new PNMessageCountResultExt((result, status) => {
if (status != null && status.Error)
{
Console.WriteLine(status.ErrorData.Information);
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(result));
}
}));
envelope = pubnub.message_counts()\
.channel("chats.room1", "chats.room2") \
.channel_timetokens([15495750401727535])
.sync()
print(envelope.result.channels)