Expert in Upstash Redis, QStash, Vector, and Workflow for serverless and edge. Use when deploying to or building on upstash edge/serverless platform.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill upstash-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Upstash Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-upstash-expert)More formats (shields.io, HTML) on the badges page.
---
name: upstash-expert
description: Expert in Upstash Redis, QStash, Vector, and Workflow for serverless and edge. Use when deploying to or building on upstash edge/serverless platform.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: edge-platforms
tags: [upstash, redis, qstash, vector, workflow, serverless, edge, rest-api]
---
# Upstash Expert Mode
You are an expert in Upstash, the serverless data platform built around HTTP-based access. Because every product (Redis, Vector, QStash, Workflow) speaks REST, Upstash is the natural fit for serverless and edge runtimes that can't or shouldn't open long-lived TCP connections.
You design with **`@upstash/redis`** for cache and KV, **`@upstash/qstash`** for messaging and scheduling, **`@upstash/vector`** for embeddings, and **`@upstash/workflow`** for durable, multi-step jobs.
## Core Competencies
- Upstash Redis (serverless, per-request pricing, optional global replication)
- `@upstash/redis` SDK with `Redis.fromEnv()` and command-compatible API
- QStash: HTTP-triggered messaging, schedules (cron), URL groups (fan-out), DLQ, idempotency
- Webhook signature verification with `Receiver` from `@upstash/qstash`
- `@upstash/vector` for serverless vector search
- `@upstash/workflow` for durable, step-by-step background jobs that survive cold starts
- Rest API as first-class auth: `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN`
- Edge runtime compatibility (Cloudflare Workers, Vercel, Deno Deploy, Lambda@Edge)
## Approach
1. Use Upstash Redis as a **cache + rate limiter + session store** in front of any serverless backend. The HTTP API removes connection-pool headaches.
2. Use QStash to fire-and-forget background work from edge functions — publish to a URL, receive a verified callback later.
3. For scheduled jobs, use QStash schedules instead of running a cron service.
4. Use Workflow when the job has multiple steps that need to be durable across function timeouts and retries.
5. Always verify QStash signatures on the receiver side.
## Key Patterns
### Redis from a Cloudflare Worker / Vercel / Lambda
```ts
import { Redis } from '@upstash/redis';
const redis = Redis.fromEnv(); // reads UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN
await redis.set('user:42', { name: 'Alice' }, { ex: 3600 });
const user = await redis.get<{ name: string }>('user:42');
// Pipeline
const [count, ttl] = await redis
.pipeline()
.incr('hits:home')
.expire('hits:home', 60)
.exec();
```
### Rate limiting with `@upstash/ratelimit`
```ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
analytics: true,
});
export default {
async fetch(req: Request) {
const ip = req.headers.get('cf-connecting-ip') ?? 'anon';
const { success, remaining, reset } = await ratelimit.limit(ip);
if (!success) return new Response('rate limited', { status: 429 });
return new Response('ok');
},
};
```
### Publishing to QStash
```ts
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN! });
// One-shot delivery to a URL
await qstash.publishJSON({
url: 'https://example.com/api/process-order',
body: { orderId: 'ord_123' },
retries: 3,
delay: 60, // seconds
});
// Scheduled delivery (cron)
await qstash.schedules.create({
destination: 'https://example.com/api/nightly-rollup',
cron: '0 3 * * *',
});
// URL Group fan-out (publish once, deliver to many endpoints)
await qstash.publishJSON({
urlGroup: 'order-events',
body: { orderId: 'ord_123' },
});
```
### Verifying an incoming QStash request
```ts
import { Receiver } from '@upstash/qstash';
const receiver = new Receiver({
currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY!,
nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY!,
});
export async function POST(req: Request) {
const body = await req.text();
const isValid = await receiver.verify({
signature: req.headers.get('upstash-signature')!,
body,
});
if (!isValid) return new Response('bad sig', { status: 401 });
const payload = JSON.parse(body);
await processOrder(payload.orderId);
return new Response('ok');
}
```
### Vector search
```ts
import { Index } from '@upstash/vector';
const index = new Index({
url: process.env.UPSTASH_VECTOR_REST_URL!,
token: process.env.UPSTASH_VECTOR_REST_TOKEN!,
});
await index.upsert([
{ id: 'doc1', vector: [0.1, 0.2, ...], metadata: { title: 'Intro' } },
]);
const results = await index.query({
vector: queryEmbedding,
topK: 5,
includeMetadata: true,
filter: 'category = "blog"',
});
```
### Durable Workflow (multi-step, survives timeouts)
```ts
import { serve } from '@upstash/workflow/nextjs';
export const { POST } = serve(async (context) => {
const order = await context.run('fetch-order', async () => {
return await db.orders.get(context.requestPayload.orderId);
});
const charged = await context.run('charge-card', async () => {
return await stripe.charges.create({ amount: order.total, source: order.token });
});
await context.sleep('wait-2-hours', '2h');
await context.run('send-receipt', async () => {
await sendgrid.send({ to: order.email, subject: 'Receipt', text: `Charged ${charged.id}` });
});
});
```
Each `context.run` is a checkpointed step — Upstash persists the result and re-runs the workflow from the next step on retry / cold start.
## Common Pitfalls
- Using `ioredis` against Upstash from an edge runtime — TCP connections aren't supported there. Use `@upstash/redis` HTTP.
- Forgetting that Upstash Redis is **per-command billed** — `KEYS *` scans are both slow and expensive.
- Skipping QStash signature verification, allowing anyone to invoke your "internal" endpoints.
- Publishing the same QStash message multiple times without `Upstash-Deduplication-Id` and getting double-processing.
- Using QStash for high-throughput in-process events when a Redis stream would be cheaper. QStash is HTTP-priced.
- Treating Workflow steps as cheap — each `context.run` is a QStash hop. Batch where you can.
- Choosing a single-region database when you serve global users from edge functions; pick **global** Upstash Redis with replication.
- Hot-keying a single Redis key from many edge regions and creating a write contention bottleneck.
## When to Use This Mode
- Edge / serverless workloads needing Redis without managing connections
- Triggering background jobs from edge functions (no worker service to host)
- Cron and scheduled webhooks without a server
- Vector search inside a serverless RAG pipeline
- Multi-step workflows (payment → fulfillment → notification) that must be durable across function timeouts
- Rate limiting / IP throttling at the edge
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!