Rate Limiting

Rate limiting controls traffic to a network resource by limiting the number of requests a user or IP can make in a specific time period. It protects against malicious traffic and denial of service (DoS) attacks by preventing system overloads, ensuring fair usage, and mitigating abuse or exploitation by attackers.

Rate limiting is available on all plans for free and is currently in beta.

Grafbase allows you to define rate limiting rules, evaluated concurrently at runtime, to be applied on incoming traffic. These rules allow you to flexibly match on request patterns that need to be throttled or even blocked in order to protect your APIs. At present, there are four well known traits at your disposal: request headers, graphql operation names, ips and jwt claims.

There's a new field, rateLimiting, available in the configuration that enables the definition of rate limiting rules.

export default config({ graph: g, rateLimiting: { rules: [], }, })

Rate limiting rules are enforced at the edge and the system was designed to be eventually consistent, it shouldn't be considered or used for accurate counters. Enforcing limit checks on the edge allows for optimal performance but with a slight cost, rules are local to the edge location. This effectively means that traffic handled for Stockholm that match a limiting condition, will not affect traffic handled for Lisbon that also match the same limiting condition.

All rules share a common structure:

  • name: a unique identifier
  • condition: traits of the request that the rule applies to. Supported values are:
    • headers
    • jwtClaims
    • operations
    • ips
  • duration: how long the rule applies, in seconds. Supported values are
    • 10
    • 60
  • limit: the maximum number of requests allowed

Matching on request headers is likely one of the most common form of rate limiting policies one can encounter. When defining rules for header matching you can optionally select to match all values of a given header or, a specific set of values.

export default config({ graph: g, rateLimiting: { rules: [ { name: 'headers', limit: 10, duration: 10, condition: { headers: [ { name: 'the-header', value: '*', }, { name: 'the-header-2', value: ['the-value'], }, ], }, }, ], }, })

The rule defined above will match requests that the have the header the-header with any value and the-header-2 with value the-value.

Another common use case is matching on specific attributes of authenticated users. Particularly, jwt claims when using jwt tokens as means of authentication and authorization.

export default config({ graph: g, rateLimiting: { rules: [ { name: 'jwtClaims', limit: 10, duration: 10, condition: { jwtClaims: [ { name: 'the-claim-0', value: '*', }, { name: 'the-claim-1', value: 'the-value', }, { name: 'the-claim-2', value: ['the-value'], }, { name: 'the-claim-3', value: { key: 'the-value' }, }, ], }, }, ], }, })

The defined above will match requests the specified claims and values, and similarly to headers, you can select to match on any claim value.

When it comes to GraphQL, it is quite common to enforce certain limits on queries and rate limiting is no exception. You can select to limit capacity on specific named queries.

export default config({ graph: g, rateLimiting: { rules: [ { name: 'specific-operations', limit: 10, duration: 10, condition: { operations: ['operation-1'], }, }, { name: 'all-operations', limit: 10, duration: 10, condition: { operations: '*', }, }, ], }, })

To limit traffic coming from specific IPs as a mitigation strategy you can leverage the specific rule that leverages the x-forwarded-for header.

export default config({ graph: g, rateLimiting: { rules: [ { name: 'specific-ips', limit: 10, duration: 10, condition: { ips: ['1.1.1.1'], }, }, { name: 'all-ips', limit: 10, duration: 10, condition: { ips: '*', }, }, ], }, })
Was this page helpful?