All skills
upstash avatar

/upstash-redis-js

@35da719
by upstashupstash/skills27 stars
7

Work with the @upstash/redis TypeScript/JavaScript SDK, a serverless HTTP-based Redis client for Next.js, Vercel, Cloudflare Workers, edge runtimes, and Node.js. Use when adding a cache (cache-aside, write-through, TTL and expiration strategies), session storage and user sessions, a key-value store, leaderboards and rankings with sorted sets, counters, distributed locks, queues with lists, streams and consumer groups, sparse index-addressed arrays and ring buffers (ARSET, ARINSERT, ARRING, ARGREP, AROP), embeddings and nearest-neighbour vector search stored inside Redis (VECTOR commands via redis.vector, separate from @upstash/vector), JSON documents, pipelines and MULTI/EXEC transactions, Lua scripting, read replicas, or full-text search, typo-tolerant search, facets, aggregations, and search over Redis stream entries with Upstash Redis Search (different from regular FT.SEARCH; also available for TCP clients via @upstash/search-redis and @upstash/search-ioredis). Also use when migrating from ioredis or node-redis, when a Redis connection is needed from a serverless function without connection pooling, when integrating @upstash/ratelimit, or when the user says Redis cache, KV store, session store, serverless Redis, or Upstash Redis. Supports automatic serialization/deserialization of JavaScript types.

Use this Skill: https://skilld.dev/gh/upstash/skills/upstash-redis-js

This session only. Nothing lands on disk.

data-structuresstreams.md

≈1.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Streams

Overview

Streams are append-only logs of entries with unique IDs. They support consumer groups for distributed processing and delivery guarantees.

Good For

  • Event sourcing
  • Activity logs
  • Message queues with multiple consumers
  • Real-time data feeds
  • Chat systems

Limitations

  • Entries cannot be modified after insertion
  • Memory grows unbounded unless trimmed
  • Maximum stream length: practical limits based on memory

Examples

import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

// Add entry to stream (auto-generated ID)
const id1 = await redis.xadd("events", "*", {
  type: "user.login",
  userId: "123",
  timestamp: Date.now(),
});
// Returns ID like "1642424242424-0"

// Add with specific ID (timestamp-sequence format)
const id2 = await redis.xadd("events", "1642424242424-1", {
  type: "user.logout",
  userId: "123",
});

// Add with trim (limit stream size)
await redis.xadd(
  "events",
  "*",
  { event: "test" },
  {
    trim: {
      type: "MAXLEN",
      comparison: "=",
      threshold: 1000,
    },
  }
);

// Add with approximate trim (more efficient)
await redis.xadd(
  "events",
  "*",
  { event: "test" },
  {
    trim: {
      type: "MAXLEN",
      comparison: "~",
      threshold: 1000,
    },
  }
);

// Get stream length
const length = await redis.xlen("events");

// Read entries from stream
const entries = await redis.xread("events", "0", { count: 10 });
// Returns: [{ name: "events", messages: [{ id: "...", message: {...} }] }]

// Read latest entries
const latest = await redis.xread("events", "$"); // $ means new entries only

// Read from multiple streams
const multi = await redis.xread(["stream1", "stream2"], ["0", "0"]);

// Note: Blocking (BLOCK option) is not yet supported in Upstash Redis

// Get range of entries by ID
const range = await redis.xrange("events", "-", "+"); // All entries
const specific = await redis.xrange("events", "1642424242424-0", "1642424242424-1");
const last10 = await redis.xrevrange("events", "+", "-", 10); // Last 10 in reverse

// Create consumer group
await redis.xgroup("events", {
  type: "CREATE",
  group: "processors",
  id: "0",
  options: { MKSTREAM: true },
});
// Creates group "processors" starting from beginning

// Read as consumer group member
const groupEntries = await redis.xreadgroup(
  "processors", // group name
  "consumer1", // consumer name
  "events", // stream key
  ">", // > means undelivered messages
  { count: 5 }
);

// Acknowledge processed messages
if (groupEntries && groupEntries[0]) {
  const messages = (groupEntries[0] as any)[1] as Array<{ 0: string; 1: any }>;
  if (messages && messages.length > 0) {
    const messageIds = messages.map((m) => m[0]);
    await redis.xack("events", "processors", messageIds);
  }
}

// Get pending messages (delivered but not acknowledged)
const pending = await redis.xpending("events", "processors", "-", "+", 10);
// Returns pending entry details

// Get detailed pending info for specific consumer
const pendingDetails = await redis.xpending(
  "events", // stream key
  "processors", // group
  "-", // start
  "_+", // end
  10, // count
  { consumer: "consumer1" } // optional: specific consumer
);

// Claim pending messages (take over from another consumer)
const claimed = await redis.xclaim(
  "events", // stream key
  "processors", // group
  "consumer2", // new owner (consumer)
  3600000, // min idle time (ms)
  "1642424242424-0" // message ID(s) to claim
);

// Delete messages
await redis.xdel("events", [id1, id2]);

// Trim stream to maximum length
await redis.xtrim("events", { strategy: "MAXLEN", threshold: 1000, exactness: "=" });
await redis.xtrim("events", { strategy: "MAXLEN", threshold: 1000, exactness: "~" }); // Approximate (more efficient)

// Get consumer group info
const groups = await redis.xinfo("events", { type: "GROUPS" });
// Returns list of consumer groups

// Get consumers in group
const consumers = await redis.xinfo("events", { type: "CONSUMERS", group: "processors" });
// Returns list of consumers and their stats

// Delete consumer from group
await redis.xgroup("events", {
  type: "DELCONSUMER",
  group: "processors",
  consumer: "consumer1",
});

// Delete consumer group
await redis.xgroup("events", { type: "DESTROY", group: "processors" });

// Practical example: Event log with processing
await redis.xadd("orders", "*", {
  orderId: "order-123",
  status: "pending",
  amount: 99.99,
});

// Create processor group
await redis.xgroup("orders", {
  type: "CREATE",
  group: "order-processors",
  id: "0",
  options: { MKSTREAM: true },
});

// Worker reads and processes
const orders = await redis.xreadgroup("order-processors", "worker-1", "orders", ">", {
  count: 1,
});

if (orders && orders[0]) {
  const messages = (orders[0] as any)[1] as Array<{ 0: string; 1: any }>;
  if (messages && messages.length > 0) {
    const order = messages[0];
    // Process order...
    console.log("Processing:", order[1]);

    // Acknowledge when done
    await redis.xack("orders", "order-processors", order[0]);
  }
}

Source: SKILL.md on GitHub

1 warningtoday3 checks · Risk SAFE
  • Gen Agent Trust Hubtoday

    The skill provides a comprehensive guide for using the Upstash Redis JavaScript SDK. It follows security best practices for credential management by advocating for environment variables and relies on official vendor resources. While the skill facilitates data ingestion from external databases, it does so as a standard database client with no malicious patterns or unexpected behaviors detected.

  • Sockettoday

    1 alert: gptSecurity

  • Snyktoday

    Risk: LOW · No issues

Signed by skilld at 35da719. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 6 days ago.

Activeupdated 2 weeks ago
metadata
{
  "author": "Upstash",
  "homepage": "https://upstash.com"
}

README badge

README badge for upstash/skills/upstash-redis-js