@contract-kit/provider-rate-limit-upstash
v0.1.4
Published
Upstash-based rate limit provider for contract-kit - adds rate limit port using Upstash Redis
Maintainers
Readme
@contract-kit/provider-rate-limit-upstash
Upstash-based implementation of the RateLimitPort for Contract Kit apps.
This provider uses Upstash Redis and the @upstash/ratelimit library to provide distributed rate limiting capabilities to your Contract Kit applications.
Features
- ✅ Implements the standard
RateLimitPortinterface - ✅ Uses Upstash Redis REST API (serverless-friendly, no connection pooling needed)
- ✅ Fixed window rate limiting algorithm
- ✅ Dynamic rate limit configuration per request
- ✅ Configurable key prefix to avoid collisions
- ✅ Type-safe configuration with Zod validation
- ✅ Works seamlessly with Contract Kit framework and middleware
Installation
npm install @contract-kit/provider-rate-limit-upstash @upstash/redis @upstash/ratelimitOr with other package managers:
bun add @contract-kit/provider-rate-limit-upstash @upstash/redis @upstash/ratelimit
pnpm add @contract-kit/provider-rate-limit-upstash @upstash/redis @upstash/ratelimit
yarn add @contract-kit/provider-rate-limit-upstash @upstash/redis @upstash/ratelimitTypeScript Requirements
This package requires TypeScript 5.0 or higher for proper type inference.
Configuration
Set these environment variables:
| Variable | Required | Description | Example |
|----------|----------|-------------|---------|
| UPSTASH_REDIS_REST_URL | Yes | Your Upstash Redis REST URL | https://us1-properly-ancient-12345.upstash.io |
| UPSTASH_REDIS_REST_TOKEN | Yes | Your Upstash Redis REST token | AXXXeyJpZCI6IjEy... |
| UPSTASH_PREFIX | No | Key prefix for rate limit keys (default: ck:ratelimit) | myapp:ratelimit |
Getting Upstash Credentials
- Sign up at Upstash
- Create a new Redis database
- Navigate to the database details page
- Copy the REST URL and REST token from the "REST API" section
Usage
With Contract Kit Framework
import { createServer } from "@contract-kit/server";
import { upstashRateLimitProvider } from "@contract-kit/provider-rate-limit-upstash";
export const app = createServer({
ports: basePorts,
providers: [upstashRateLimitProvider],
// ... rest of your config
});In Middleware or Policies
Once the provider is registered, you can use the rate limit port in your middleware:
// Example middleware that rate limits by IP address
async function rateLimitMiddleware(ctx: AppCtx) {
const result = await ctx.ports.rateLimit.hit({
key: `ip:${ctx.ip}`,
limit: 100,
windowSec: 60, // 100 requests per 60 seconds
});
if (result.limited) {
return {
status: 429,
headers: {
"X-RateLimit-Limit": "100",
"X-RateLimit-Remaining": String(result.remaining ?? 0),
"X-RateLimit-Reset": result.resetAt?.toISOString() ?? "",
},
body: {
error: "Too Many Requests",
message: "Rate limit exceeded. Please try again later.",
},
};
}
// Request is allowed
return undefined; // continue to next middleware/handler
}Different Rate Limits for Different Endpoints
You can apply different rate limits for different operations:
// Strict rate limit for auth endpoints
const loginResult = await ctx.ports.rateLimit.hit({
key: `login:${ctx.ip}`,
limit: 5,
windowSec: 300, // 5 attempts per 5 minutes
});
// More relaxed rate limit for API endpoints
const apiResult = await ctx.ports.rateLimit.hit({
key: `api:user:${userId}`,
limit: 1000,
windowSec: 3600, // 1000 requests per hour
});Using with Contract Metadata
You can define rate limit metadata on your contracts:
const getTodos = contract.get("/todos")
.meta({
rateLimit: { key: "api", limit: 60, windowSec: 60 },
});Then in your middleware, read this metadata and apply rate limits:
async function rateLimitFromMeta(ctx: AppCtx, meta?: any) {
if (!meta?.rateLimit) return;
const { key, limit, windowSec } = meta.rateLimit;
const result = await ctx.ports.rateLimit.hit({
key: `${key}:${ctx.userId ?? ctx.ip}`,
limit,
windowSec,
});
if (result.limited) {
throw ctx.err.TooManyRequests();
}
}Rate Limit Result
The hit method returns a RateLimitResult with:
interface RateLimitResult {
limited: boolean; // true if the request should be rejected
remaining: number | null; // requests remaining in the window
resetAt: Date | null; // when the window resets
}Implementation Details
- Algorithm: Uses fixed window rate limiting via
Ratelimit.fixedWindow() - Backend: Upstash Redis REST API (serverless-compatible)
- Per-request configuration: Creates a new
Ratelimitinstance for eachhit()call to support dynamic limits - Key prefix: Configurable prefix to avoid key collisions
Advanced Usage
Access the Underlying Redis Client
The provider extends the standard RateLimitPort with access to the underlying Upstash Redis client:
import type { UpstashRateLimitPort } from "@contract-kit/provider-rate-limit-upstash";
const rateLimit = ctx.ports.rateLimit as UpstashRateLimitPort;
// Access the Redis client for advanced operations
await rateLimit.client.get("some:key");
await rateLimit.client.set("some:key", "value");Testing
The provider includes comprehensive tests. Run them with:
bun testLicense
MIT
