Documentation Index
Fetch the complete documentation index at: https://mintlify.com/get-convex/rate-limiter/llms.txt
Use this file to discover all available pages before exploring further.
This guide walks you through setting up and using the Convex Rate Limiter component with real examples.
Install the Package
First, install the rate limiter component:npm install @convex-dev/rate-limiter
Configure convex.config.ts
Create or update convex/convex.config.ts to register the component:import { defineApp } from "convex/server";
import rateLimiter from "@convex-dev/rate-limiter/convex.config.js";
const app = defineApp();
app.use(rateLimiter);
export default app;
Define Your Rate Limits
Create a new file (e.g., convex/rateLimits.ts) and define your rate limits:import { RateLimiter, MINUTE, HOUR } from "@convex-dev/rate-limiter";
import { components } from "./_generated/api";
export const rateLimiter = new RateLimiter(components.rateLimiter, {
// A per-user limit, allowing one every ~6 seconds.
// Allows up to 3 in quick succession if they haven't sent many recently.
sendMessage: { kind: "token bucket", rate: 10, period: MINUTE, capacity: 3 },
// One global / singleton rate limit for free trial signups
freeTrialSignUp: { kind: "fixed window", rate: 100, period: HOUR },
});
Rate limit strategies:
- Token bucket: Tokens are added at a steady rate, allowing bursts up to
capacity
- Fixed window: All tokens granted at once every
period, with optional rollover
Use in a Mutation - Global Rate Limit
Apply a global rate limit that applies to all users:import { mutation } from "./_generated/server";
import { rateLimiter } from "./rateLimits";
export const signUpForFreeTrial = mutation({
args: { /* ... */ },
handler: async (ctx, args) => {
// Check global rate limit for free trial signups
const { ok, retryAfter } = await rateLimiter.limit(ctx, "freeTrialSignUp");
if (!ok) {
throw new Error(
`Rate limit exceeded. Please try again in ${Math.ceil(retryAfter! / 1000)} seconds.`
);
}
// Proceed with signup logic...
},
});
Use in a Mutation - Per-User Rate Limit
Apply a rate limit specific to each user using a key:import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { rateLimiter } from "./rateLimits";
export const sendMessage = mutation({
args: {
text: v.string(),
},
handler: async (ctx, args) => {
const user = await ctx.auth.getUserIdentity();
if (!user) {
throw new Error("Not authenticated");
}
// Rate limit per user
const { ok, retryAfter } = await rateLimiter.limit(ctx, "sendMessage", {
key: user.subject,
});
if (!ok) {
throw new Error(
`You're sending messages too quickly. Please wait ${Math.ceil(retryAfter! / 1000)} seconds.`
);
}
// Save the message
await ctx.db.insert("messages", {
text: args.text,
userId: user.subject,
timestamp: Date.now(),
});
},
});
Error Handling with throws Option
For cleaner code, use the throws option to automatically throw errors when rate limits are exceeded:import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { RateLimiter, HOUR, isRateLimitError } from "@convex-dev/rate-limiter";
import { components } from "./_generated/api";
const rateLimiter = new RateLimiter(components.rateLimiter, {
failedLogins: { kind: "token bucket", rate: 10, period: HOUR },
});
export const login = mutation({
args: {
email: v.string(),
password: v.string(),
},
handler: async (ctx, args) => {
try {
// Automatically throw if rate limit is exceeded
await rateLimiter.limit(ctx, "failedLogins", {
key: args.email,
throws: true,
});
// Attempt login...
const success = await attemptLogin(args.email, args.password);
if (success) {
// Reset rate limit on successful login
await rateLimiter.reset(ctx, "failedLogins", { key: args.email });
}
return { success };
} catch (error) {
if (isRateLimitError(error)) {
throw new Error(
`Too many failed login attempts. Please try again later.`
);
}
throw error;
}
},
});
function attemptLogin(email: string, password: string) {
// Your login logic here
return true;
}
When using throws: true, the rate limiter throws a ConvexError with data {kind: "RateLimited", name, retryAfter}. Use isRateLimitError() to check if an error is a rate limit error.
What You’ve Learned
You now know how to:
- Install and configure the Convex Rate Limiter component
- Define rate limits with different strategies (token bucket and fixed window)
- Apply global rate limits that affect all users
- Apply per-user rate limits using keys
- Handle rate limit errors gracefully
- Reset rate limits when needed
Next Steps
Explore more advanced features:
- Custom token counts: Consume multiple tokens in a single request for LLM token limiting
- Sharding: Scale to high throughput with configurable sharding
- Reservations: Reserve capacity to avoid starvation on larger requests
- React hook: Check rate limits from your frontend with
useRateLimit
For detailed information, check out the full Stack post on rate limiting.