Usage
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "<UPSTASH_REDIS_URL>",
token: "<UPSTASH_REDIS_TOKEN>",
});
const entries = await redis.xrange("events", "-", "+");
Parameters
The key of the stream to read from.
The minimum entry ID to include:
"-" - Start from the first entry in the stream
"<ms>-<seq>" - Start from this specific ID (inclusive)
"<ms>" - Start from this timestamp (equivalent to "<ms>-0")
The maximum entry ID to include:
"+" - Include all entries up to the last one
"<ms>-<seq>" - Include entries up to this specific ID (inclusive)
"<ms>" - Include entries up to this timestamp
Maximum number of entries to return.
Response
result
Record<string, Record<string, unknown>>
An object where keys are entry IDs and values are objects containing the entry’s field-value pairs.The SDK automatically deserializes values and attempts to parse JSON.Returns an empty object {} if no entries match the range.
Examples
Get all entries
// Add some entries
await redis.xadd("events", "*", { user: "alice", action: "login" });
await redis.xadd("events", "*", { user: "bob", action: "logout" });
await redis.xadd("events", "*", { user: "charlie", action: "update" });
// Get all entries
const all = await redis.xrange("events", "-", "+");
console.log(all);
// Returns:
// {
// "1678901234567-0": { user: "alice", action: "login" },
// "1678901234568-0": { user: "bob", action: "logout" },
// "1678901234569-0": { user: "charlie", action: "update" }
// }
Get entries in a time range
// Add entries over time
const id1 = await redis.xadd("events", "1000-0", { event: "first" });
const id2 = await redis.xadd("events", "2000-0", { event: "second" });
const id3 = await redis.xadd("events", "3000-0", { event: "third" });
const id4 = await redis.xadd("events", "4000-0", { event: "fourth" });
// Get entries between timestamp 1500 and 3500
const range = await redis.xrange("events", "1500", "3500");
console.log(range);
// Returns:
// {
// "2000-0": { event: "second" },
// "3000-0": { event: "third" }
// }
Get entries starting from a specific ID
const id1 = await redis.xadd("events", "*", { n: 1 });
const id2 = await redis.xadd("events", "*", { n: 2 });
const id3 = await redis.xadd("events", "*", { n: 3 });
// Get entries from id2 onwards
const entries = await redis.xrange("events", id2, "+");
// Returns entries with id2 and id3
Get entries up to a specific ID
// Get entries from beginning up to id2
const entries = await redis.xrange("events", "-", id2);
// Returns entries with id1 and id2
Limit number of entries
// Add many entries
for (let i = 0; i < 100; i++) {
await redis.xadd("events", "*", { index: i });
}
// Get only first 10 entries
const limited = await redis.xrange("events", "-", "+", 10);
const count = Object.keys(limited).length;
console.log(`Received ${count} entries`); // 10
Get entries from last hour
const now = Date.now();
const oneHourAgo = now - (60 * 60 * 1000);
const recent = await redis.xrange(
"events",
`${oneHourAgo}-0`, // Start from 1 hour ago
`${now}-0` // Up to now
);
Iterate through entries
const entries = await redis.xrange("events", "-", "+");
for (const [id, data] of Object.entries(entries)) {
console.log(`Entry ${id}:`, data);
}
// Or get specific fields
for (const [id, data] of Object.entries(entries)) {
const timestamp = id.split("-")[0];
const date = new Date(parseInt(timestamp));
console.log(`${date.toISOString()}: ${data.action}`);
}
let lastId = "-";
const pageSize = 10;
// Get first page
const page1 = await redis.xrange("events", lastId, "+", pageSize);
const ids = Object.keys(page1);
if (ids.length > 0) {
lastId = ids[ids.length - 1]; // Last ID from this page
}
// Get next page (entries after lastId)
const page2 = await redis.xrange("events", `(${lastId}`, "+", pageSize);
// Note: Use "(" prefix for exclusive range (Redis 6.2+)
Get entries in reverse order
// XRANGE returns entries in ascending order
const ascending = await redis.xrange("events", "-", "+");
// To get descending order, use XREVRANGE
const descending = await redis.xrevrange("events", "+", "-");
// (Note: XREVRANGE not shown in source, but available in Redis)
Working with complex data
// Add entries with complex data
await redis.xadd("orders", "*", {
orderId: "ORD-123",
userId: 42,
items: JSON.stringify(["item1", "item2"]),
total: 99.99,
metadata: JSON.stringify({ source: "web", campaign: "summer" })
});
// Read and parse
const orders = await redis.xrange("orders", "-", "+");
for (const [id, order] of Object.entries(orders)) {
const items = JSON.parse(order.items as string);
const metadata = JSON.parse(order.metadata as string);
console.log({
id,
orderId: order.orderId,
userId: order.userId,
items,
total: order.total,
metadata
});
}
Important Notes
Inclusive Ranges
Both start and end are inclusive:
await redis.xadd("stream", "1000-0", { n: 1 });
await redis.xadd("stream", "2000-0", { n: 2 });
await redis.xadd("stream", "3000-0", { n: 3 });
const result = await redis.xrange("stream", "1000-0", "2000-0");
// Returns BOTH 1000-0 and 2000-0 (inclusive)
Special Range Values
| Value | Meaning |
|---|
"-" | The minimum ID in the stream |
"+" | The maximum ID in the stream |
"<ms>" | Shorthand for "<ms>-0" |
Entry ID Format
Entry IDs are strings in the format "<milliseconds>-<sequence>":
const entries = await redis.xrange("events", "-", "+");
for (const id of Object.keys(entries)) {
const [ms, seq] = id.split("-");
const timestamp = parseInt(ms);
const date = new Date(timestamp);
console.log(`Entry at ${date.toISOString()}, sequence ${seq}`);
}
The SDK deserializes the response into a clean object:
// Raw Redis response:
// [["1678901234567-0", ["field1", "value1", "field2", "value2"]]]
// SDK returns:
{
"1678901234567-0": {
field1: "value1",
field2: "value2"
}
}
Automatic JSON Parsing
The SDK attempts to parse JSON values:
await redis.xadd("stream", "*", {
name: "test",
data: JSON.stringify({ nested: "object" })
});
const entries = await redis.xrange("stream", "-", "+");
const firstEntry = Object.values(entries)[0];
console.log(firstEntry.data);
// Returns: { nested: "object" } (already parsed)
- Use
count to limit results for large streams
- Specific time ranges are faster than full scans (
"-" to "+")
- For reverse order, consider using
XREVRANGE if available
- Pagination is efficient when using the last ID from previous page