Using YARP as an API gateway and rate limiter

Using YARP as an API gateway and rate limiter

In this post, we’re taking a look at YARP (Yet Another Reverse Proxy) as a way to rate limit your APIs by using it as an API gateway.

Last week I posted a blog about rate limiting in your C# applications. If you haven’t read that article yet, check it out here.

I concluded that article with the following:

If you’re working with more complex architectures and multiple services that interact with each other, it’s often a better idea to look at API gateway tools such as Azure API Management or YARP. These allow you to manage and configure rate limiting for multiple destinations in one place.

So let’s take a look at the better way of handling your rate limiting by leveraging the power of YARP!

YARP (Yet Another Reverse Proxy)

If you don’t know what YARP is I already did a small introduction article here. In essence, it comes down to being a reverse proxy with a whole bunch of other features tagged on. It’s being developed by Microsoft internally and is fully open-source.

Reverse proxy example

All these additional features like rate limiting, authentication, gRPC, … have made YARP a viable alternative to the likes of Azure API Management. Azure API Management is a lot more feature-rich, but not everyone needs all those bells and whistles.

In return, you get a hugely customizable proxy that runs just like any other .NET Core Web API project. It doesn’t require you to learn entirely new concepts. Just use your existing C# and .NET knowledge to configure anything to your liking!

API Gateway and Rate Limiting

As I said in my previous blog post, rate limiting is an essential part of protecting anything you build that’s exposed to the outside world. And honestly, it doesn’t hurt to have it set up for internal systems either. Better safe than sorry, right?

But setting up rate limiting on a per API basis means it can get pretty messy if you have multiple instances of the same API running, or if you want to adjust the limiters on multiple APIs at once.

That’s where API Gateways can help. These act as a facade you set up that every incoming and outgoing request has to pass through. This means you can set up authentication, load balancing, and much more in one place. Making things easier to manage and to oversee.

Rate Limiting in YARP

If you’re new to YARP, take a look at their Getting Started Guide or just clone the Basic YARP Sample. Now let’s set up rate limiting!

Solution Architecture

In this example, we have an API running .NET Framework. It’s our ‘legacy’ system, which we’re replacing bit by bit using the Strangler Pattern. Our new code is in a .NET 8.0 Web API.

We want our clients to simply be able to call a single API Gateway that will forward the requests to either our legacy API or our brand-new, fresh off the presses and 100% test-coverage API (heh). Here’s what that looks like:

Reverse proxy example

As you can see, we’ve only been able to migrate our /orders path to our new API. Everything else still needs to go to our legacy system.

YARP Configuration

If we look at the above architecture drawing, that means our YARP config needs to look like this:

{
  "ReverseProxy": {
    "Routes": {
      "route1": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/orders/{**catch-all}"
        }
      },
      "route2": {
        "ClusterId": "cluster2",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "cluster1": {
        "Destinations": {
          "cluster1/destination1": {
            "Address": "https://new-api.mydomain.com/"
          }
        }
      },
      "cluster2": {
        "Destinations": {
          "cluster2/destination1": {
            "Address": "https://legacy-api.mydomain.com/"
          }
        }
      }
    }
  }
}

YARP will first check if the route is under the /order path. In case it is, the request gets forwarded to https://new-api.mydomain.com/. In all other cases, the requests get forwarded to https://legacy-api.mydomain.com/. Your client sees nothing of this, and just calls https://api.mydomain.com/.

Setting up Rate limiting

Now let’s configure rate limiting. To do this we need to use the rate-limiting middleware we talked about in the last blogpost. Yes, the same one as if you would put rate limiting on a single .NET Web API!

Let’s define a simple fixed window rate limiter in our program.cs:

builder.Services.AddRateLimiter(_ => _
    .AddFixedWindowLimiter(policyName: "FixedRateLimiter", options =>
    {
        options.Window = TimeSpan.FromSeconds(12);
        options.PermitLimit = 4;
        options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        options.QueueLimit = 2;
    }));

Also, don’t forget to add the rate limiter middleware:

app.UseRateLimiter();

Our rate limiter is ready to go. Now we just need to set the routes it applies to. If we only want to apply it to our legacy system, our config is going to look like this:

{
  "ReverseProxy": {
    "Routes": {
      "route1": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/orders/{**catch-all}"
        }
      },
      "route2": {
        "ClusterId": "cluster2",
        "RateLimiterPolicy": "FixedRateLimiter",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "cluster1": {
        "Destinations": {
          "cluster1/destination1": {
            "Address": "https://new-api.mydomain.com/"
          }
        }
      },
      "cluster2": {
        "Destinations": {
          "cluster2/destination1": {
            "Address": "https://legacy-api.mydomain.com/"
          }
        }
      }
    }
  }
}

We just had to add a single line: "RateLimiterPolicy": "FixedRateLimiter". Easy right?

Final Thoughts

As we saw, using rate limiting in YARP is very straightforward. We can use the knowledge we already have about .NET to configure YARP to our liking. A little bit of configuration et voila! It’s all set up and ready to go.

So in conclusion, if you’re looking for an API gateway but Azure API Management is outside your budget, why not give YARP a try? And if you’re interested in learning more about YARP and .NET, why not subscribe to my newsletter?

YARP!