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.

patternsleaderboard.md

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

Leaderboard Pattern

Overview

Use Sorted Sets (ZSET) to implement leaderboards with automatic ranking. Scores determine rank, members are unique.

Good For

  • Gaming leaderboards (high scores)
  • User rankings by activity, points, or reputation
  • Top performers, trending content
  • Time-based rankings (using timestamps as scores)

Limitations

  • Ties have undefined order (use score decimals or timestamps to break ties)
  • Memory grows with number of members

Examples

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

const redis = Redis.fromEnv();

// Add/update scores
await redis.zadd("leaderboard:global", { score: 1500, member: "player:123" });
await redis.zadd("leaderboard:global", { score: 2300, member: "player:456" });
await redis.zadd("leaderboard:global", { score: 1800, member: "player:789" });

// Increment score
await redis.zincrby("leaderboard:global", 100, "player:123"); // +100 points

// Get top 10 (highest scores first)
const top10 = await redis.zrange("leaderboard:global", 0, 9, { rev: true, withScores: true });
// Returns: [{ member: "player:456", score: 2300 }, { member: "player:789", score: 1800 }, ...]

// Get player's rank (0-based, lowest score = rank 0)
const rank = await redis.zrevrank("leaderboard:global", "player:123");
// Use zrevrank for highest-first ranking

// Get player's score
const score = await redis.zscore("leaderboard:global", "player:123");

// Get rank with score
async function getPlayerStats(playerId: string) {
  const [rank, score, totalPlayers] = await Promise.all([
    redis.zrevrank("leaderboard:global", playerId),
    redis.zscore("leaderboard:global", playerId),
    redis.zcard("leaderboard:global"),
  ]);

  return {
    playerId,
    score,
    rank: rank !== null ? rank + 1 : null, // Convert to 1-based
    totalPlayers,
  };
}

// Get surrounding players (context ranking)
async function getRankContext(playerId: string, range: number = 5) {
  const rank = await redis.zrevrank("leaderboard:global", playerId);

  if (rank === null) return null;

  const start = Math.max(0, rank - range);
  const end = rank + range;

  const players = await redis.zrange("leaderboard:global", start, end, {
    rev: true,
    withScores: true,
  });

  return players;
}

// Time-based leaderboard (daily)
const today = new Date().toISOString().split("T")[0];
const dailyKey = `leaderboard:daily:${today}`;

await redis.zadd(dailyKey, { score: 500, member: "player:123" });
await redis.expire(dailyKey, 86400 * 7); // Keep for 7 days

// Multiple leaderboards (by region)
await redis.zadd("leaderboard:us", { score: 1500, member: "player:123" });
await redis.zadd("leaderboard:eu", { score: 1500, member: "player:456" });

// Get top from multiple boards
const [usTop, euTop] = await Promise.all([
  redis.zrange("leaderboard:us", 0, 9, { rev: true, withScores: true }),
  redis.zrange("leaderboard:eu", 0, 9, { rev: true, withScores: true }),
]);

// Score range query (players with score 1000-2000)
const midRange = await redis.zrange("leaderboard:global", 1000, 2000, {
  byScore: true,
  withScores: true,
});

// Remove player
await redis.zrem("leaderboard:global", "player:123");

// Remove bottom 10% (cleanup low performers)
const total = await redis.zcard("leaderboard:global");
const cutoff = Math.floor(total * 0.1);
await redis.zpopmin("leaderboard:global", cutoff);

// Tie breaking: use timestamp as decimal
const now = Date.now();
const scoreWithTieBreaker = 1500 + now / 1e13; // 1500.000123456
await redis.zadd("leaderboard:global", {
  score: scoreWithTieBreaker,
  member: "player:999",
});

// Batch update scores
const pipeline = redis.pipeline();
pipeline.zadd("leaderboard:global", { score: 1600, member: "player:1" });
pipeline.zadd("leaderboard:global", { score: 1700, member: "player:2" });
pipeline.zadd("leaderboard:global", { score: 1800, member: "player:3" });
await pipeline.exec();

// Get percentile rank
async function getPercentile(playerId: string) {
  const [rank, total] = await Promise.all([
    redis.zrevrank("leaderboard:global", playerId),
    redis.zcard("leaderboard:global"),
  ]);

  if (rank === null) return null;

  return ((rank / total) * 100).toFixed(2);
}

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 5 days ago.

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

README badge

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