ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
    Develop in the portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
API Management
    Overview
    API Keys
    Rate Limiting
      Getting startedHow it works
      Policies
      Guides
        Dynamic rate limitingCombining policiesPer-user rate limitsMonitoring & troubleshooting
    Caching
    GraphQL
    Monetization
    Policies
      Overview
      Authentication
      Authorization
      Security & Validation
      Metrics, Billing & Quotas
      Testing
      Request Modification
      Response Modification
      Upstream Authentication
      GraphQL
      Caching
      Other
      Guides
        Multiple Auth PoliciesPer-user rate limitsComposite Policy PatternsClient mTLSCloud Workload Identity
    Handlers
AI Gateway
MCP Gateway
MCP Server
Developer Portal
Development
Deploying & Source Control
Analytics
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Guides

Per-user rate limiting using a database and the ZoneCache

This example shows a more advanced implementation of dynamic rate limiting. It uses a database lookup to get the customer details and combines that with the ZoneCache to improve performance, reduce latency and lower the load on the database.

This example uses Supabase as the database, but you could use your own API, Xata, or Firebase. The implementation is similar for all.

If you haven't already, check out the rate-limiting policy and the dynamic rate limiting guide. Then you should be oriented to how dynamic rate limiting works.

Below is a full implementation of a custom rate limiting function. In this example it is a module called per-user-rate-limiting.ts.

Code
import { CustomRateLimitDetails, ZoneCache, ZuploContext, ZuploRequest, environment, } from "@zuplo/runtime"; import { createClient } from "@supabase/supabase-js"; const CACHE_NAME = "rate-limit-requests-allowed-cache"; const SB_URL = "https://YOUR_SUPABASE_URL.supabase.co"; const SB_SERVICE_ROLE_KEY = environment.SB_SERVICE_ROLE_KEY; const FALLBACK_REQUESTS_ALLOWED = 100; export async function rateLimitKey( request: ZuploRequest, context: ZuploContext, policyName: string, ): Promise<CustomRateLimitDetails> { // Get the customer ID from the user data. // This might be from a JWT or API Key metadata. // Ensure an authentication policy runs before this. const customerId = request.user?.data?.customerId; if (!customerId) { context.log.error("No customerId found on request.user.data"); return { key: request.user?.sub ?? "unknown", requestsAllowed: FALLBACK_REQUESTS_ALLOWED, timeWindowMinutes: 1, }; } // We don't want to hit the database on every request // So we'll use the fast zone cache to cache this data const cache = new ZoneCache(CACHE_NAME, context); let requestsAllowed = await cache.get(customerId); // If we didn't get a value, we'll need to go to the database // In this example we're using supabase, but you could use your // own API, Xata, etc. if (requestsAllowed === undefined) { // create the supabase client and read the customer's const supabase = createClient(SB_URL, SB_SERVICE_ROLE_KEY); let { data, error } = await supabase .from("customer_rate_limits") .select("requestsAllowed") .eq("customerId", customerId); // If something goes wrong, we probably want to log an // error and assume a default, vs go down if (error) { context.log.error(error); requestsAllowed = FALLBACK_REQUESTS_ALLOWED; } else { context.log.info(data); requestsAllowed = data[0].requestsAllowed; } // store the read value in the ZoneCache // do this asynchronously to improve performance cache .put(customerId, requestsAllowed, 60) .catch((err) => context.log.error(err)); } return { key: customerId, requestsAllowed: requestsAllowed, timeWindowMinutes: 1, }; }

The above function can be applied to a rate limiter with the following configuration in policies

Code
{ "name": "my-per-user-rate-limit-policy", "policyType": "rate-limit-inbound", "handler": { "export": "RateLimitInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "rateLimitBy": "function", "requestsAllowed": 100, "timeWindowMinutes": 1, "identifier": { "export": "rateLimitKey", "module": "$import(./modules/per-user-rate-limiting)" } } } }
Edit this page
Last modified on June 11, 2026
Combining policiesMonitoring & troubleshooting
TypeScript
JSON