Documentation Index
Fetch the complete documentation index at: https://redop.useagents.site/docs/llms.txt
Use this file to discover all available pages before exploring further.
Use Upstash Ratelimit when you want Redis-backed rate limiting with serverless-friendly HTTP APIs, analytics, caching, and optional multi-region support.
This guide adapts Upstash’s TypeScript Ratelimit SDK to a Redop server.
Install
Add the Upstash packages to your Redop app:
bun add @upstash/ratelimit @upstash/redis
Prerequisites
You need:
- an Upstash Redis database
UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN
Create the limiter
Create the limiter once at module scope so blocked-request caching can work across hot requests.
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,
prefix: "@redopjs/upstash-ratelimit",
});
Add it as Redop middleware
Use global middleware when you want one policy across many tools, resources, or prompts.
import { McpError, McpErrorCode, Redop } from "@redopjs/redop";
new Redop({
serverInfo: {
name: "upstash-demo",
version: "0.1.0",
},
})
.middleware(async ({ request, next }) => {
const identifier =
request.headers["x-user-id"] ??
request.headers["x-forwarded-for"] ??
request.ip ??
"anonymous";
const { pending, remaining, reset, success } =
await ratelimit.limit(identifier);
if (pending) {
void pending.catch(() => {});
}
if (!success) {
throw new McpError(
McpErrorCode.InvalidRequest,
`Rate limit exceeded. Retry after ${Math.ceil(
Math.max(reset - Date.now(), 0) / 1000
)} seconds.`
);
}
return next();
})
.tool("ping", {
handler: () => ({ ok: true }),
});
Identify callers carefully
Use a stable identifier for the type of traffic you want to control:
- API key for per-key limits
- tenant ID for per-tenant limits
- authenticated user ID for per-user limits
- IP address only when you do not have a better identity
If you want the rate-limit result available to handlers, store it in request context with derive(...).
const app = new Redop<{ rateLimit: { remaining: number; reset: number } }>()
.derive(async ({ request }) => {
const identifier =
request.headers["x-user-id"] ?? request.ip ?? "anonymous";
const { pending, remaining, reset, success } =
await ratelimit.limit(identifier);
if (pending) {
void pending.catch(() => {});
}
if (!success) {
throw new Error("Too many requests");
}
return {
rateLimit: { remaining, reset },
};
});
What Upstash adds
According to the Upstash Ratelimit TypeScript docs, notable features include:
- in-memory blocking cache for hot serverless instances
- timeout handling
- analytics and dashboard support
- traffic protection and deny lists
- multiple limits and dynamic limits
- optional multi-region replication
If you enable analytics or multi-region sync, Upstash also returns a pending promise. In serverless runtimes, use the runtime’s background-wait primitive when available so those async tasks can finish.
When to choose Upstash
Upstash is a strong fit when you already use Upstash Redis or want Redis-backed control with features like analytics, deny lists, or multi-region behavior.
See also