Usage
import { Redis } from "@upstash/redis" ;
const redis = new Redis ({
url: "<UPSTASH_REDIS_URL>" ,
token: "<UPSTASH_REDIS_TOKEN>" ,
});
const id = await redis . xadd ( "events" , "*" , {
user: "alice" ,
action: "login" ,
timestamp: Date . now ()
});
Parameters
The entry ID. Can be:
"*" - Fully automatic ID generation (recommended)
"<ms>-<seq>" - Explicit ID (e.g., "1526919030474-55")
"<ms>-*" - Auto-generate sequence number for the given millisecond timestamp (Redis 8+)
IDs must be greater than previous entries in the stream.
entries
Record<string, unknown>
required
The field-value pairs to store in the stream entry. Values are automatically serialized to strings.
Optional configuration: Don’t create the stream if it doesn’t exist. Returns null if the stream doesn’t exist.
Automatically trim the stream to limit its size: Show trim by maximum length
{
type : "MAXLEN" | "maxlen" ,
threshold : number ,
comparison : "=" | "~" ,
limit ?: number // Only with "~"
}
Trim to keep at most threshold entries. {
type : "MINID" | "minid" ,
threshold : string ,
comparison : "=" | "~" ,
limit ?: number // Only with "~"
}
Trim to remove entries older than threshold ID.
comparison: "=" - Exact trimming (slower)
comparison: "~" - Approximate trimming (faster, recommended)
limit - Maximum number of entries to trim (only with "~")
Response
The ID of the added entry (e.g., "1526919030474-0"). Returns null if nomkStream: true and the stream doesn’t exist.
Examples
Add entry with automatic ID
const id = await redis . xadd ( "user-events" , "*" , {
userId: "123" ,
action: "page_view" ,
page: "/dashboard"
});
console . log ( id );
// Returns: "1678901234567-0"
Add multiple entries
const ids = await Promise . all ([
redis . xadd ( "logs" , "*" , { level: "info" , message: "Server started" }),
redis . xadd ( "logs" , "*" , { level: "warn" , message: "High memory usage" }),
redis . xadd ( "logs" , "*" , { level: "error" , message: "Connection failed" })
]);
console . log ( ids );
// Returns: [
// "1678901234567-0",
// "1678901234568-0",
// "1678901234569-0"
// ]
Store complex data
const event = {
userId: 42 ,
action: "purchase" ,
product: "premium-plan" ,
amount: 99.99 ,
metadata: JSON . stringify ({ referral: "email" , campaign: "spring-sale" })
};
const id = await redis . xadd ( "purchases" , "*" , event );
Use explicit ID
// Use current timestamp with sequence 0
const timestamp = Date . now ();
const id = await redis . xadd ( "events" , ` ${ timestamp } -0` , {
type: "custom" ,
data: "test"
});
Trim stream by maximum length
// Keep only the most recent 1000 entries
const id = await redis . xadd (
"events" ,
"*" ,
{ user: "alice" , action: "click" },
{
trim: {
type: "MAXLEN" ,
comparison: "~" , // Approximate trimming (efficient)
threshold: 1000
}
}
);
Trim stream by minimum ID
// Remove entries older than 1 hour
const oneHourAgo = Date . now () - ( 60 * 60 * 1000 );
const id = await redis . xadd (
"events" ,
"*" ,
{ user: "bob" , action: "logout" },
{
trim: {
type: "MINID" ,
comparison: "~" ,
threshold: ` ${ oneHourAgo } -0`
}
}
);
Don’t create stream if it doesn’t exist
const id = await redis . xadd (
"maybe-exists" ,
"*" ,
{ data: "test" },
{ nomkStream: true }
);
if ( id === null ) {
console . log ( "Stream doesn't exist" );
} else {
console . log ( `Added with ID: ${ id } ` );
}
Limit trimming operations
// Trim at most 100 entries per operation
const id = await redis . xadd (
"events" ,
"*" ,
{ user: "charlie" , action: "update" },
{
trim: {
type: "MAXLEN" ,
comparison: "~" ,
threshold: 1000 ,
limit: 100 // Don't trim more than 100 in one operation
}
}
);
Stream IDs Explained
Stream IDs have the format: <milliseconds>-<sequence>
milliseconds - Unix timestamp in milliseconds
sequence - Sequence number for entries added in the same millisecond
Example: 1678901234567-0
Timestamp: 1678901234567 (March 15, 2023)
Sequence: 0 (first entry in that millisecond)
Automatic Generation
Using "*" automatically generates an ID:
const id1 = await redis . xadd ( "stream" , "*" , { data: "first" });
const id2 = await redis . xadd ( "stream" , "*" , { data: "second" });
// If added in the same millisecond:
// id1: "1678901234567-0"
// id2: "1678901234567-1" (sequence incremented)
// If added in different milliseconds:
// id1: "1678901234567-0"
// id2: "1678901234568-0" (new timestamp, sequence reset)
ID Ordering
IDs must always increase:
// ✅ Valid - IDs increase
await redis . xadd ( "stream" , "1000-0" , { data: "first" });
await redis . xadd ( "stream" , "1000-1" , { data: "second" });
await redis . xadd ( "stream" , "1001-0" , { data: "third" });
// ❌ Error - ID must be greater than last entry
await redis . xadd ( "stream" , "1001-0" , { data: "fourth" });
await redis . xadd ( "stream" , "1000-5" , { data: "fifth" }); // Error!
Important Notes
Use comparison: "~" (approximate) for better performance
Exact trimming ("=") may block for large deletions
Set limit to prevent long blocking operations
Memory Management
Without trimming, streams grow indefinitely. Consider:
// Time-based retention (keep last 24 hours)
const yesterday = Date . now () - ( 24 * 60 * 60 * 1000 );
await redis . xadd ( "events" , "*" , data , {
trim: { type: "MINID" , comparison: "~" , threshold: ` ${ yesterday } -0` }
});
// Size-based retention (keep last 10,000 entries)
await redis . xadd ( "events" , "*" , data , {
trim: { type: "MAXLEN" , comparison: "~" , threshold: 10000 }
});