> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modelence.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Rate Limiting

Modelence provides a built-in rate limiting system you can use to protect any mutation or query from abuse. Authentication endpoints come with [their own default limits](/authentication/rate-limiting).

## Defining Rate Limits

You can define your own rate limits by adding a `rateLimits` array to a [Module](/api-reference/modelence/server/classes/Module). Each rule specifies a bucket name, the type of actor being limited (`ip` or `user`), a time window, and a maximum number of allowed calls within that window.

```typescript theme={null}
import { Module } from 'modelence/server';
import { time } from 'modelence';

const myModule = new Module('myFeature', {
  rateLimits: [
    {
      bucket: 'myAction',
      type: 'ip',
      window: time.minutes(15),  // 15-minute window
      limit: 10,                 // max 10 calls per window
    },
  ],
  // ...
});
```

Multiple rules can share the same bucket to enforce more than one window. All rules on a bucket are checked — if any one is exceeded, the call is rejected:

```typescript theme={null}
rateLimits: [
  { bucket: 'myAction', type: 'ip', window: time.minutes(1),  limit: 3  },  // 3 per minute
  { bucket: 'myAction', type: 'ip', window: time.hours(1),    limit: 20 },  // 20 per hour
  { bucket: 'myAction', type: 'ip', window: time.days(1),     limit: 100 }, // 100 per day
],
```

### Consuming a Rate Limit

Call [`consumeRateLimit`](/api-reference/modelence/server/functions/consumeRateLimit) inside a mutation or query handler to check and increment the rate limit counter. It throws a [RateLimitError](/api-reference/modelence/index/classes/RateLimitError) automatically when any matching rule is exceeded:

```typescript theme={null}
import { consumeRateLimit } from 'modelence/server';

// Inside a mutation handler:
async function handleMyAction(args, { connectionInfo }) {
  await consumeRateLimit({
    bucket: 'myAction',
    type: 'ip',
    value: connectionInfo.ip,
  });

  // ... proceed with the action
}
```

An optional `message` parameter lets you provide a user-facing error message instead of the default:

```typescript theme={null}
await consumeRateLimit({
  bucket: 'myAction',
  type: 'ip',
  value: connectionInfo.ip,
  message: 'Too many requests. Please wait a moment and try again.',
});
```

## Reverse Proxy Deployments

For backward compatibility, Modelence trusts the complete `X-Forwarded-For`
chain when no proxy configuration is present. This preserves existing client IP
behavior during upgrades, but callers can supply their own value. Production
deployments should list only the proxy addresses or networks they control:

```typescript theme={null}
import { startApp } from 'modelence/server';

startApp({
  security: {
    trustedProxies: ['loopback', '10.0.0.0/8'],
  },
});
```

For hosted deployments, the equivalent environment variable takes precedence
over the `startApp()` setting:

```bash theme={null}
MODELENCE_TRUSTED_PROXIES="loopback,10.0.0.0/8"
```

`trustedProxies` accepts IP addresses, CIDR ranges, and the Express named ranges
`loopback`, `linklocal`, and `uniquelocal`. Modelence then walks the forwarded
chain from the app toward the client and uses the first untrusted address.
Forged values prepended by a caller do not become the IP rate-limit key.

Configure the edge proxy to overwrite or append the actual peer address, and
prevent untrusted clients from connecting directly to the application port.

### Cloudflare

Cloudflare *appends* to an inbound `X-Forwarded-For` header rather than
overwriting it, so its left-most value is chosen by the caller. Cloudflare
therefore [recommends](https://developers.cloudflare.com/fundamentals/reference/http-headers/)
reading `CF-Connecting-IP`, which always holds exactly one address. Set
`clientIpHeader` to use it:

```typescript theme={null}
startApp({
  security: {
    trustedProxies: [/* Cloudflare's published IP ranges */],
    clientIpHeader: 'cf-connecting-ip',
  },
});
```

`clientIpHeader` is only read when the connecting peer matches `trustedProxies`,
so it must be configured alongside them with Cloudflare's
[published IP ranges](https://www.cloudflare.com/ips/). Two caveats:

* With the default trust-all behavior (neither `trustedProxies` nor
  `MODELENCE_TRUSTED_PROXIES` set), every peer counts as trusted, so anyone able
  to reach the application port directly could set the header themselves. Always
  pair `clientIpHeader` with an explicit trusted proxy list.
* If the trusted list does not cover the full Cloudflare edge, requests through
  uncovered addresses fall back to the peer address, collapsing many clients
  onto a single rate-limit key.

`True-Client-IP` works the same way, but is Enterprise-only and must be enabled
via a Managed Transform. Cloudflare warns that in a stacked-CDN setup its value
can be spoofed unless your own edge sets it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.