Skip to main content

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

Add response metadata

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