[Jens Neuse](/content/people/jens-neuse/index.html)

CEO & Co-Founder at WunderGraph

February 11, 2024·14min read

Edited on May 12, 2026 by [Brendan Bondurant](/content/people/brendan-bondurant/index.html)

## TL;DR

Traditional rate limiting does not work well for federated GraphQL because one client request can trigger many subgraph requests. The piece argues that rate limiting should happen at the Router, not only at the edge or inside each subgraph, because the Router has full operation context and can enforce limits centrally. Cosmo Router uses Redis to track subgraph request counts across router instances, reject abusive traffic, and return clear rateLimit feedback so clients know how many requests remain and when to retry.

**Key points:**

- One GraphQL request can fan out into hundreds of subgraph calls, so counting only client requests is too coarse for Federation.
- Rate limiting at the edge or inside each subgraph either lacks full context or requires complex, distributed state.
- Cosmo Router rate limits federated GraphQL APIs based on the number of subgraph requests per time window, using Redis to share counters across router instances.
- Clients get transparent feedback via a `rateLimit` object in the GraphQL `extensions` field, including remaining requests and retry-after hints.

## Why traditional rate limiting solutions don't work for federated GraphQL APIs

### Why doesn't "requests per minute" rate limiting work for federated GraphQL APIs?

Rate limiting is a common technique to protect your APIs from abuse. It's not new and there are many solutions out there that can help you to implement rate limiting for your APIs. However, existing rate limiting solutions are not designed to work with federated GraphQL APIs. To be able to rate limit federated GraphQL APIs, you need to understand how a GraphQL query is executed. There's no simple 1:1 relationship between client request and Microservice requests like in REST APIs.

In a federated GraphQL API, one client request can result in hundreds or even thousands of requests to your Microservices. This is because the client can query multiple fields from multiple services in a single request. What is often described as an advantage of GraphQL, can also be seen as a challenge when it comes to securing your API.

Here's an example of a federated GraphQL query:

```graphql
{
  employee(id: 1) {
    id
    details {
      forename
      surname
    }
    hobbies {
      __typename
    }
  }
}
```

This query requests the `employee` field from the `EmployeeService` and the `hobbies` field from the `HobbyService`. The `EmployeeService` and the `HobbyService` are two separate Microservices with their own GraphQL schema. When the client sends this query to the API Gateway, the API Gateway will forward the request to the `EmployeeService` and the `HobbyService`.

A traditional rate limiting solution would count this as one request when in reality it results in two requests to two different Microservices. But this can get even more complex, e.g. when we're fetching nested fields for not just one but multiple entities. Cosmo Router is able to efficiently batch nested requests like this, but the load on your Microservices might still increase.

## The risks of not rate limiting federated GraphQL APIs

### What can go wrong if I don't rate limit a federated GraphQL API?

The problem with not rate limiting your federated GraphQL APIs is that the nature of GraphQL makes it very easy to craft a query that can result in a high load on your Microservices. What looks like a single request on the network level might actually be a batch of requests. Take a look at the following example Operation:

```graphql
query DDoS {
  a: employee(id: 1) {
    ...EmployeeDetails
  }
  b: employee(id: 2) {
    ...EmployeeDetails
  }
  c: employee(id: 3) {
    ...EmployeeDetails
  }
  d: employee(id: 4) {
    ...EmployeeDetails
  }
  e: employee(id: 5) {
    ...EmployeeDetails
  }
  aa: employee(id: 6) {
    ...EmployeeDetails
  }
  bb: employee(id: 7) {
    ...EmployeeDetails
  }
  ccc: employee(id: 8) {
    ...EmployeeDetails
  }
}

fragment EmployeeDetails on Employee {
  id
  details {
    forename
    surname
  }
  hobbies {
    __typename
  }
}
```

Although this GraphQL Operation is still a single network request with a small payload, it results in 8*3=24 requests to load the employee id, details and hobbies for 8 employees.

| Location                     | Pros                                              | Cons                                                         |
|------------------------------|---------------------------------------------------|--------------------------------------------------------------|
| Edge (CDN/Workers)          | Easy to set up; good for generic attacks         | No full query context; distributed limits are hard          |
| Subgraph/Microservice        | Close to databases; protects local resources      | No global view; duplicated logic per team/service           |
| Router (Cosmo)              | Full operation + subgraph context; one place     | Requires central router + Redis                               |

## Rate Limiting Federated GraphQL APIs at the Edge

### Should I implement rate limiting for GraphQL Federation at the edge (e.g. Cloudflare, CDN)?

If we can agree that rate limiting federated GraphQL APIs is important, the next question is where to implement it. We can implement rate limiting at the Edge, e.g. using Cloudflare Workers, at the API Gateway / Router level, or within the Microservices themselves.

The advantage of implementing rate limiting at the Edge is that it's very easy to set up and it can protect your API from some types of attacks. However, "on the Edge" usually means "far away" from your Microservices. If you'd run your Federated GraphQL API Gateway / Router on the Edge, you'd have a lot of overhead and latency for every request between Router and Microservices.

So, if we don't want to "execute" a federated GraphQL Operation on the Edge, this means that our "Edge Router" won't be able to see the actual requests that are being made to the Microservices. As a consequence, it won't be able to rate limit the actual requests, but can only "approximate" what the load on the Microservices might be.

What are the consequences of imprecise rate limiting? If your rate limiting is too lax, you might still be vulnerable to DDoS attacks. If it's too strict, you might block legitimate requests.

Approximating the load is not a great solution because an attacker can carefully craft a GraphQL Operation that passes the rate limit check but still results in a high load on your Microservices. But there's another reason why rate limiting at the Edge is not ideal for federated GraphQL APIs.

If an attacker needs to be authenticated to send a request to your API, so they create an API key and attack your API from multiple regions simultaneously, e.g. by using a botnet. If you want to block this attacker, you'd have to detect the attack and block the API key. How do you do this? You'd have to share the state of the rate limit across all Edge Routers. Otherwise, the attacker could surpass the rate limit by staying below the rate limit on each Edge Router, but exceeding it in total.

If we share state across multiple Edge Routers, we're essentially building a distributed rate limiting system. A distributed rate limiting system is not just extremely complex to build and maintain, it's also either eventually consistent or very slow if it wants to be consistent. So what's the point of having a distributed rate limiting system if it's not precise, or precise but slow?

Edge Workers are great to protect an API from more generic attacks, but when it comes to rate limiting federated GraphQL APIs, this is not the right place to do it.

## Rate Limiting Federated GraphQL APIs at the Subgraph / Microservice level

### Is it a good idea to put rate limiting logic into each GraphQL subgraph?

Ok, so what about implementing rate limiting within the Microservices themselves? On the one hand, this is a great place to implement rate limiting because we're close to the most expensive resources that we want to protect, like databases and other external services. On the other hand, a Subgraph lacks the context of the entire federated GraphQL Operation. This means that while we're able to rate limit the sub-request to our service, we're not able to protect the federated GraphQL API as a whole.

Furthermore, by implementing rate limiting within the Microservices, we create an organizational problem. The rate limiting logic is now spread across multiple services which are owned by different teams. First, we need to bring the knowledge of implementing rate limiting to all teams. Then we have to find consensus on how to implement rate limiting and how to report rate limiting violations to the API Gateway / Router. In addition, we have to implement and maintain the rate limiting logic in multiple services, and each team needs to run and operate their own rate limiting infrastructure, e.g. Redis.

Wouldn't it be great if we could implement rate limiting for federated GraphQL APIs in a single place, without having to modify the Microservices themselves?

This would allow us to have a single source of truth for rate limiting, save us from having to implement and maintain rate limiting logic in multiple services, and allow us to have a global view of the rate limiting state. In addition, we don't have to find consensus on how to implement it across all teams, and we don't have to spread the knowledge of implementing rate limiting across the whole organization.

Instead, we can enable a single platform team to implement and maintain rate limiting as a centralized service for all teams and services.

## Rate Limiting Federated GraphQL APIs with Cosmo Router & Redis

### How does Cosmo Router implement rate limiting for federated GraphQL APIs using Redis?

With Cosmo Router, you can implement rate limiting for federated GraphQL APIs in a single place, close to your Subgraphs, but without having to modify them, and without having to implement and maintain rate limiting logic in multiple services.

The Router is the component in your federated GraphQL API architecture that generates and executes the federated GraphQL Operation. This means that the Router has the full context of the GraphQL Operation, knows which Subgraphs are involved and which fields are being requested from which Subgraph. With all this information, the Router can accurately rate limit and therefore protect your federated GraphQL API.

Compared to rate limiting at the Edge, the Router is much closer to the Microservices, and even if you're running a cluster of Routers, you can easily share the state of the rate limit across all Routers using a fast in-memory store like Redis.

In contrast to rate limiting within the Microservices, the Router has the full request context and has a lot of other advantages as discussed in the previous sections.

How does rate limiting work with Cosmo Router and Redis? Cosmo Router uses Redis under the hood to store the rate limit state for a given key. Depending on the configuration of the rate limit, the Router will increment the counter for the given key and check if the counter exceeds the limit. If the counter exceeds the limit, the Router will either raise an error for this particular field (partial rate limiting) or reject the Operation as a whole.

### Conclusion

We've discussed the importance of rate limiting for (federated) GraphQL APIs and why traditional rate limiting solutions don't work for GraphQL APIs. We've explored the risks of not applying rate limiting.

Next, we've looked at the different places where you can implement rate limiting. Rate limiting can be implemented at the Edge, at the Subgraph / Microservice level, or at the API Gateway / Router level. Each of these places has its own advantages and disadvantages.

Finally, we've explored the different rate limiting algorithms and why we've chosen a simple strategy for Cosmo Router.
