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.
Overview
The getValue() method returns the current state of a rate limit without consuming any tokens. It provides detailed information including the current token value, timestamp, shard number, and configuration.
Method Signature
async getValue<Name extends string = keyof Limits & string>(
ctx: RunQueryCtx,
name: Name,
...options: Name extends keyof Limits & string
? [
WithKnownNameOrInlinedConfig<
Limits,
Name,
{
key?: string;
sampleShards?: number;
}
>?,
]
: [
WithKnownNameOrInlinedConfig<
Limits,
Name,
{
key?: string;
sampleShards?: number;
}
>,
]
): Promise<GetValueReturns>
Parameters
The context object from a query, including runQuery. This method works in both queries and mutations.
The name of the rate limit to query.
Optional configuration for retrieving the value.The key for the rate limit instance. If not provided, retrieves the shared global rate limit value.
For sharded rate limits, the number of shards to sample. This affects which shard’s data is returned. Useful for debugging or monitoring specific shards.
The rate limit configuration. Required only if the rate limit name was not defined in the RateLimiter constructor.
Return Type
Returns an object with the following properties:The current number of available tokens. This can be a decimal for token bucket rate limits.
The timestamp (in milliseconds) of when this value was last updated. For token bucket, this is the last refill time. For fixed window, this is the start of the current window.
The shard number that this data came from. Useful for sharded rate limits to understand which shard is being used.
The complete rate limit configuration for this limit, including all settings like kind, rate, period, capacity, etc.
Use with calculateRateLimit
The getValue() method pairs perfectly with the calculateRateLimit helper function for client-side rate limit tracking:
import { query } from "./_generated/server";
import { rateLimiter } from "./rateLimiter";
export const getMessageLimitStatus = query({
handler: async (ctx) => {
const userId = await getCurrentUser(ctx);
return await rateLimiter.getValue(ctx, "sendMessage", {
key: userId,
});
},
});
Then on the client:
import { calculateRateLimit } from "@convex-dev/rate-limiter";
import { useQuery } from "convex/react";
import { api } from "./convex/_generated/api";
function MessageComposer() {
const limitStatus = useQuery(api.messages.getMessageLimitStatus);
if (!limitStatus) return null;
// Calculate current state on the client
const current = calculateRateLimit(
{ value: limitStatus.value, ts: limitStatus.ts },
limitStatus.config,
Date.now()
);
return (
<div>
<p>Available tokens: {Math.floor(current.value)}</p>
{current.retryAfter && (
<p>Next token in: {Math.ceil(current.retryAfter / 1000)}s</p>
)}
</div>
);
}
Examples
Basic Usage
import { query } from "./_generated/server";
import { rateLimiter } from "./rateLimiter";
export const checkApiQuota = query({
handler: async (ctx) => {
const userId = await getCurrentUser(ctx);
const status = await rateLimiter.getValue(ctx, "apiCalls", {
key: userId,
});
return {
available: Math.floor(status.value),
lastUpdated: new Date(status.ts).toISOString(),
config: status.config,
};
},
});
Monitoring Dashboard
export const getRateLimitStats = query({
handler: async (ctx) => {
await requireAdmin(ctx);
// Get stats for multiple rate limits
const sendMessageGlobal = await rateLimiter.getValue(
ctx,
"sendMessage"
);
const signupGlobal = await rateLimiter.getValue(ctx, "freeTrialSignUp");
return {
sendMessage: {
available: sendMessageGlobal.value,
shard: sendMessageGlobal.shard,
},
signup: {
available: signupGlobal.value,
shard: signupGlobal.shard,
},
};
},
});
User Quota Display
export const getUserQuotas = query({
handler: async (ctx) => {
const userId = await getCurrentUser(ctx);
const [messages, uploads] = await Promise.all([
rateLimiter.getValue(ctx, "sendMessage", { key: userId }),
rateLimiter.getValue(ctx, "upload", { key: userId }),
]);
return {
messages: {
available: Math.floor(messages.value),
max: messages.config.capacity ?? messages.config.rate,
refillRate: `${messages.config.rate} per ${messages.config.period}ms`,
},
uploads: {
available: Math.floor(uploads.value),
max: uploads.config.capacity ?? uploads.config.rate,
},
};
},
});
Debug Specific Shard
export const debugRateLimitShard = query({
args: { shardNumber: v.number() },
handler: async (ctx, args) => {
await requireAdmin(ctx);
const status = await rateLimiter.getValue(ctx, "sendMessage", {
sampleShards: args.shardNumber,
});
return {
shard: status.shard,
value: status.value,
timestamp: status.ts,
age: Date.now() - status.ts,
config: status.config,
};
},
});
Calculate Time Until Next Token
import { calculateRateLimit } from "@convex-dev/rate-limiter";
export const getTimeUntilNextToken = query({
handler: async (ctx) => {
const userId = await getCurrentUser(ctx);
const status = await rateLimiter.getValue(ctx, "sendMessage", {
key: userId,
});
// Use calculateRateLimit to project future state
const current = calculateRateLimit(
{ value: status.value, ts: status.ts },
status.config,
Date.now()
);
if (current.value >= 1) {
return { ready: true };
}
return {
ready: false,
waitMs: current.retryAfter,
currentTokens: current.value,
};
},
});
Notes
Unlike check(), which evaluates whether an action would be allowed, getValue() returns the raw state data. Use getValue() when you need to display quota information or perform custom calculations.
The value returned can be a decimal number for token bucket rate limits, as tokens accumulate gradually over time. Use Math.floor() if you need a whole number.
For rate limits that haven’t been used yet, getValue() will return the initial state with full capacity.
The ts field has different meanings depending on the rate limit type:
- Token bucket: The last time tokens were calculated/updated
- Fixed window: The start time of the current window