Messaging – Fetch Past Messages
Overview
Fetching past messages allows a Kotlin application to retrieve historical messages that were previously published to a topic. This API is used when real-time delivery alone is not sufficient, such as during application startup, recovery after downtime, or when running analytics over earlier events.
Unlike subscriptions, fetching message history does not create a live stream. It is a point-in-time query against messages that are still retained by RelayX. The result is a finite list of messages that match the query criteria at the time the request is executed.
This API interacts with RelayX's message storage layer. It is constrained by message retention limits, topic validity rules, and connection state. It is designed to complement subscriptions, not replace them.
How This Works
When history() is called, the Kotlin SDK sends a request to the RelayX backend to retrieve messages that match the provided topic and time range.
RelayX immediately evaluates the request against:
- The topic or wildcard pattern
- The specified start and end timestamps
- The current retention window of the account
If the client is connected, RelayX returns all matching messages that still exist. If the client is disconnected or reconnecting, the SDK returns an empty list. No retry or buffering is performed for history requests.
A history request is considered successful if it returns a list. An empty list does not necessarily indicate an error. It may mean there were no messages, the messages have expired, or the client was not connected at the time of the request.
API Usage
Method
suspend fun history(topic: String, start: Long, end: Long?): List<Message>
Note: history() is a suspend function and must be called from a coroutine scope.
Parameters
-
topic (String, required) A valid topic name or wildcard pattern. Topic rules and restrictions apply.
-
start (Long, required) The beginning of the time range for fetching messages, specified as a Unix timestamp in milliseconds.
-
end (Long?, optional)
- The end of the time range for fetching messages, specified as a Unix timestamp in milliseconds.
- This must be later than
start. - If not specified, history from
startto now will be returned.
Return Value
- Returns a
List<Message>containing message objects - Returns an empty list if:
- No messages match
- Messages have expired
- The client is disconnected or reconnecting
Message Object Structure
data class Message(
val id: String?, // Message ID
val room: String?, // Topic the message was published to
val message: Any?, // The actual message payload
val start: Long? // Timestamp when message was created (milliseconds)
)
Code Example
import relay.Realtime
import relay.models.RealtimeConfig
import relay.Message
import kotlinx.coroutines.runBlocking
import java.io.File
import java.util.concurrent.TimeUnit
val realtime = Realtime()
realtime.apiKey = "<API KEY>"
realtime.secretKey = "<SECRET KEY>"
realtime.filesDir = File.createTempFile("test", "dir").parentFile!!
val config = RealtimeConfig()
realtime.init(config)
runBlocking {
realtime.connect()
val now = System.currentTimeMillis()
// Set start time to 4 days ago (in milliseconds)
val fourDaysAgo = now - TimeUnit.DAYS.toMillis(4)
// Set end time to 2 days ago (in milliseconds)
val twoDaysAgo = now - TimeUnit.DAYS.toMillis(2)
// Fetch history with start and end time
val history = realtime.history("chat.room1", fourDaysAgo, twoDaysAgo)
// OR
// This will return messages from fourDaysAgo to now
val historyToNow = realtime.history("chat.room1", fourDaysAgo, null)
// Process the results
for (message in history) {
println("Message ID: ${message.id}")
println("Topic: ${message.room}")
println("Content: ${message.message}")
println("Timestamp: ${message.start}")
println("---")
}
}
Structure of the returned List<Message>:
// List of Message objects
[
Message(
id = "<MESSAGE UUID>",
room = "<TOPIC MATCHING TOPIC / WILDCARD TOPIC>",
message = <Actual message as String, Number, JsonObject, or JsonArray>,
start = <Timestamp in milliseconds>
),
// ... more messages
]
Failure & Edge Cases
Disconnected Client
If history() is called while the client is disconnected or reconnecting, the SDK returns an empty list. No exception is thrown.
Invalid Topics
An error is thrown if:
- The topic name is invalid
- A reserved system topic is used
Invalid Time Range
An error is thrown if:
endis earlier than or equal tostart
Expired Messages
Messages that exceed their TTL are permanently deleted. Fetching outside the retention window always returns an empty list.
Common Mistakes
-
Assuming history waits for reconnection
History requests do not retry. Call history only after a successful connection. -
Treating empty results as an error An empty list is a valid response in many scenarios.
-
Using history instead of subscriptions
History does not stream new messages. Use subscriptions for live data. -
Requesting data outside retention limits
Messages older than the plan’s retention window cannot be fetched.
Notes & Limitations
- History requests are bounded by message retention and plan limits.
- Large time ranges may return large lists and impact memory usage.
- History does not participate in reconnect replay logic.
- Timestamps must be specified in milliseconds since Unix epoch.
Join our Discord server, post your concern & someone from our team will help you out ✌️