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.

searchoverview.md

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

Redis Search

Overview

Redis Search is a full-text search and secondary indexing extension for Upstash Redis. It provides powerful APIs for querying, filtering, and aggregating data stored in Redis keys. Built on Tantivy, it supports text search with stemming, fuzzy matching, faceted navigation, and complex aggregations.

Good For

  • Full-text search over Redis data (strings, JSON, hashes)
  • Filtering and sorting with type-safe queries
  • Aggregations and analytics (averages, histograms, facets)
  • Autocomplete and typo-tolerant search
  • Faceted navigation (e-commerce categories, filters)

Packages

  • @upstash/redis - The primary SDK. Access search via redis.search (works over HTTP)
  • @upstash/search-redis - Adapter for the redis (node-redis) TCP client
  • @upstash/search-ioredis - Adapter for the ioredis TCP client

All three packages expose the same search API. See adapters.md for TCP client setup.

Schema

Schemas define which fields are indexed and how. Use the s schema builder for type-safe definitions:

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

const redis = Redis.fromEnv();

const index = await redis.search.createIndex({
  name: "products",
  prefix: "product:",
  dataType: "json",
  schema: s.object({
    name: s.string(), // TEXT - full-text searchable
    description: s.string().noStem(), // TEXT without stemming
    sku: s.string().noTokenize(), // TEXT stored as-is (no splitting)
    price: s.number("F64"), // floating point number
    stock: s.number("U64"), // unsigned 64-bit integer
    inStock: s.boolean(), // boolean
    createdAt: s.date(), // date
    category: s.keyword(), // exact-match keyword
    brand: s.facet(), // facet for aggregations
  }),
});

Field Types

Builder Redis Type TypeScript Use Case
s.string() TEXT string Full-text searchable text
s.number() F64/U64/I64 number Numeric values, ranges
s.boolean() BOOL boolean True/false filtering
s.date() DATE string Date range queries
s.keyword() KEYWORD string Exact match, lexicographic range
s.facet() FACET string Faceted aggregations
s.object({}) (nested) object Nested field groups

Field Options

  • .noTokenize() - (TEXT only) Don't split on whitespace/punctuation. Use for SKUs, URLs, emails
  • .noStem() - (TEXT only) Don't reduce words to stems. Use for brand names, proper nouns
  • .fast() - (BOOL, DATE) Enable fast filtering
  • .from("fieldName") - Map index field to a different field name in the stored data

Data Types

  • "json" - Index JSON documents stored with redis.json.set() or redis.set(). Supports nested schemas with s.object()
  • "string" - Index JSON strings stored with redis.set(). Supports nested schemas
  • "hash" - Index Redis hashes stored with redis.hset(). Flat schemas only (no nesting)
  • "stream" - Index entries added with redis.xadd(). Flat schemas only; use stream for the exact stream key instead of prefix

Commands

For detailed usage of each command category, see:

Pitfalls

Data is upserted with regular Redis commands, not through search

There is no index.upsert() or index.add() method. You store data using standard Redis commands (set, json.set, hset, or xadd for stream indexes), and the search index automatically picks up keys matching its prefix (or entries of its stream).

// Create the index
const index = await redis.search.createIndex({
  name: "users",
  prefix: "user:",
  dataType: "json",
  schema: s.object({ name: s.string(), age: s.number("U64") }),
});

// Upsert data with regular Redis commands
await redis.json.set("user:1", "$", { name: "Alice", age: 30 });
await redis.json.set("user:2", "$", { name: "Bob", age: 25 });

// Wait for the index to process the new data
await index.waitIndexing();

// Now you can query
const results = await index.query({
  filter: { name: { $eq: "Alice" } },
});

Always call waitIndexing after data changes

Index updates are batched. After upserting or deleting data, the index may not immediately reflect the changes. Call waitIndexing() to block until all pending documents are processed.

// Batch upsert many documents
for (const product of products) {
  await redis.json.set(`product:${product.id}`, "$", product);
}

// Call waitIndexing ONCE after all upserts (not after each one)
await index.waitIndexing();

// Now queries will return up-to-date results
const results = await index.query({ filter: { category: { $eq: "electronics" } } });

Tokenization splits text at word boundaries

TEXT fields are tokenized by default: "hello-world" becomes ["hello", "world"]. An $eq filter for "hello-world" matches because it finds the substring. But if you need exact matching of the full string (e.g., SKUs, URLs), use .noTokenize().

Stemming reduces words to roots

By default, TEXT fields apply language-specific stemming: "running" is stored as "run". This means $regex patterns won't match the original form. Disable with .noStem() for brand names or when you need exact word forms.

$mustNot cannot be used alone

$mustNot filters only exclude documents. Using $mustNot alone returns no results. Always combine it with $must or $should:

// Won't work - returns nothing
{ $mustNot: [{ status: { $eq: "archived" } }] }

// Correct - exclude within a broader match
{ $must: [{ category: { $eq: "electronics" } }], $mustNot: [{ status: { $eq: "archived" } }] }

SCOREFUNC and ORDERBY are mutually exclusive

You cannot use scoreFunc and orderBy in the same query. Use orderBy for deterministic sorting, scoreFunc for relevance-based ranking.

Resources

Source: SKILL.md on GitHub

1 warning10d3 checks · Risk SAFE
  • Gen Agent Trust Hub10d

    The skill provides a comprehensive set of documentation and code examples for using the Upstash Redis SDK. It covers various data structures, performance optimizations, and search capabilities. No malicious code, obfuscation, or unauthorized data access patterns were detected. The skill uses standard environment variables for secret management and references official Upstash packages.

  • Socket10d

    1 alert: gptSecurity

  • Snyk10d

    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