Skip to main content

useRateLimit

Location: src/hooks/useRateLimit.js

Overview

Provides hooks and utilities for checking and enforcing rate limits on the frontend. Includes useRateLimit for general rate limit enforcement, useRateLimitStatus for querying current rate limit state, and utility functions for detecting and parsing rate limit errors. Each action type has user-friendly error messages.

Exports

RATE_LIMIT_ACTIONS

Action type constants used across the application:

ConstantValueDescription
FRIEND_REQUEST'friend_request'Sending friend requests
MESSAGE_NEW_CONVERSATION'message_new_conversation'Starting new conversations
POST_CREATE'post_create'Creating posts
LOGIN_ATTEMPT'login_attempt'Login attempts
PROFILE_VIEW'profile_view'Viewing profiles
REPORT_SUBMIT'report_submit'Submitting reports
CIRCLE_CREATE'circle_create'Creating circles
CIRCLE_INVITE'circle_invite'Sending circle invitations

isRateLimitError (function)

function isRateLimitError(error: any): boolean

Checks if an error is a rate limit error by looking for "Rate limit exceeded", "too many", or "Try again" in the message.

parseRateLimitError (function)

function parseRateLimitError(error: any, actionType: string): string

Returns a user-friendly error message for the given action type.

useRateLimitStatus

Queries the current rate limit status for a specific action type.

function useRateLimitStatus(actionType: string): {
status: object | null,
loading: boolean,
error: object | null,
checkStatus: () => Promise<void>,
getMessage: () => string | null,
isLimited: boolean,
remaining: number | null,
resetAt: Date | null
}
ParameterTypeRequiredDescription
actionTypestringYesThe rate limit action type to check
PropertyTypeDescription
statusobject | nullRaw status from get_rate_limit_status RPC
isLimitedbooleanWhether the limit has been reached
remainingnumber | nullNumber of remaining actions allowed
resetAtDate | nullWhen the rate limit resets
getMessage() => string | nullReturns appropriate message (exceeded or remaining count)
checkStatus() => Promise<void>Manually check the current status

useRateLimit

General-purpose rate limit enforcement hook.

function useRateLimit(): {
rateLimitError: string | null,
checkLimit: (actionType: string, targetId?: string | null) => Promise<{ allowed: boolean, message: string | null, remaining?: number }>,
recordAction: (actionType: string, targetId?: string | null) => Promise<void>,
handleError: (error: any, actionType: string) => boolean,
clearError: () => void,
isRateLimitError: (error: any) => boolean
}
PropertyTypeDescription
rateLimitErrorstring | nullCurrent rate limit error message
checkLimit(actionType, targetId?) => Promise<Result>Check rate limit via check_rate_limit RPC. Fails open (allows action) if the check itself errors.
recordAction(actionType, targetId?) => Promise<void>Record a completed action via record_rate_limit_action RPC
handleError(error, actionType) => booleanCheck if an error is rate-limit-related and set the error message. Returns true if it was a rate limit error.
clearError() => voidClear the rate limit error state
isRateLimitError(error) => booleanCheck if an error is a rate limit error

Usage

import { useRateLimit, RATE_LIMIT_ACTIONS } from '../hooks/useRateLimit';

function CreatePostButton({ onPost }) {
const { checkLimit, recordAction, rateLimitError, clearError } = useRateLimit();

const handlePost = async (content) => {
const { allowed, message } = await checkLimit(RATE_LIMIT_ACTIONS.POST_CREATE);
if (!allowed) return;

const result = await createPost(content);
if (result.success) {
await recordAction(RATE_LIMIT_ACTIONS.POST_CREATE);
onPost(result);
}
};

return (
<div>
{rateLimitError && <p className="error">{rateLimitError}</p>}
<button onClick={handlePost}>Post</button>
</div>
);
}

Notes

  • Rate limit checks fail open (allow the action) if the RPC call itself fails, preventing rate limit infrastructure issues from blocking normal usage.
  • User-friendly messages are provided for each action type (e.g., "You've sent too many friend requests today. Please try again tomorrow.").
  • recordAction should be called after a successful action, not before.
  • Profile view tracking was intentionally removed as it violates Invariant #16 (AI Never Observes Individuals).

Last updated: 2026-02-07