Implementing an API Gateway For Microservices With YARP

Implementing an API Gateway For Microservices With YARP

6 min read ·

Thank you to our sponsors who keep this newsletter free to the reader:

Today's issue is sponsored by Treblle. Treblle is a lightweight SDK that helps engineering and product teams build, ship & maintain REST-based APIs faster. Simple integration for all popular languages & frameworks, including .NET 6.

And by IcePanel. IcePanel realigns your engineering & product teammates on the technical decisions made across the business. Explain your complex software systems with an interactive map that teammates can drill down to the level of detail they need.

Large Microservice-based systems can consist of tens or even hundreds of individual services. A client application needs to have all of this information to be able to make requests to the relevant microservice directly.

However, this has numerous issues, such as security concerns, increased complexity, and coupling.

We can solve this by introducing an API gateway that acts as a reverse proxy to accept API calls from the client application and forward them to the appropriate service.

The API gateway also enforces security and ensures scalability and high availability.

In this week's newsletter, I'll show you how to implement an API gateway for your microservices system using the YARP reverse proxy.

Here's what we will cover:

  • Difference between API gateway and reverse proxy
  • Installing and configuring YARP
  • Creating an API gateway with YARP
  • Authentication and rate limiting on the API gateway

Let's dive in.

What's The Difference Between an API Gateway And a Reverse Proxy?

A reverse proxy and an API gateway are similar concepts, but they serve different purposes.

A reverse proxy acts as an intermediary between clients and servers. The clients can only call the backend servers through the reverse proxy, which forwards the request to the appropriate server. It hides the implementation details of individual servers inside the internal network.

A reverse proxy is commonly used for:

  • Load balancing
  • Caching
  • Security
  • SSL termination

An API gateway is a specific type of reverse proxy designed for managing APIs. It acts as a single entry point for API consumers to the various backend services.

The key characteristics of an API gateway are:

  • Request routing and composition
  • Request/response transformations
  • Authentication and authorization
  • Rate limiting
  • Monitoring

Also, note that an API gateway can perform load balancing and other functionalities mentioned for reverse proxies.

Now let's see how to use a reverse proxy to implement an API gateway.

Installing And Configuring YARP

YARP (Yet Another Reverse Proxy) is a library developed by Microsoft to address the needs of various teams needing to build a reverse proxy. It's open source and built with .NET, so it integrates nicely with the existing ecosystem.

Let's install Yarp.ReverseProxy NuGet package to get started:

Install-Package Yarp.ReverseProxy

Next, we're going to call:

  • AddReverseProxy to add the required services for YARP
  • LoadFromConfig to load the reverse proxy configuration from application settings
  • MapReverseProxy to introduce the reverse proxy middleware
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

app.MapReverseProxy();

app.Run();

We need to tell the YARP reverse proxy how to route the incoming requests to the individual microservices.

YARP uses the concept of Routes to represent request patterns for the proxy and Clusters to represent the services to forward those requests.

{
    "ReverseProxy": {
        "Routes": {
            ...
        },
        "Clusters": {
            ...
        }
    }
}

Here's an example YARP configuration with a {**catch-all} pattern that will route all incoming requests to the one destination server.

{
  "ReverseProxy": {
    "Routes": {
      "ROUTE_NAME": {
        "ClusterId": "CLUSTER_NAME",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "CLUSTER_NAME": {
        "Destinations": {
          "destination1": {
            "Address": "https://www.milanjovanovic.tech/"
          }
        }
      }
    }
  }
}

Implementing an API Gateway With YARP

We can use YARP to build an API gateway by providing the configuration for the services we want to route traffic to.

I created a sample API gateway implementation with YARP on GitHub, so you can give it a try. The system has two services, the Users.Api and Products.Api, which are .NET 7 applications.

If a request comes in matching the /users-service/{**catch-all}, for example /users-service/users, it will be routed to the users-cluster. The same logic applies for the products-cluster. We can apply more advanced transformations through the Transforms section.

{
  "ReverseProxy": {
    "Routes": {
      "users-route": {
        "ClusterId": "users-cluster",
        "Match": {
          "Path": "/users-service/{**catch-all}"
        },
        "Transforms": [{ "PathPattern": "{**catch-all}" }]
      },
      "products-route": {
        "ClusterId": "products-cluster",
        "Match": {
          "Path": "/products-service/{**catch-all}"
        },
        "Transforms": [{ "PathPattern": "{**catch-all}" }]
      }
    },
    "Clusters": {
      "users-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:5201/"
          }
        }
      },
      "products-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:5101/"
          }
        }
      }
    }
  }
}

We now have a functioning API gateway built with YARP, routing requests to two individual services.

But what else can we do with YARP?

Adding Authentication

The API gateway can enforce authentication and authorization at the entry point to the system before letting authenticated requests proceed.

And YARP supports integrating with the existing authentication & authorization middleware.

You first need to define an authorization policy:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("authenticated", policy =>
        policy.RequireAuthenticatedUser());
});

And call UseAuthentication and UseAuthorization to add the respective middleware to the request pipeline. It's important to add them before calling MapReverseProxy.

app.UseAuthentication();

app.UseAuthorization();

app.MapReverseProxy();

Now all you have to do is add the AuthorizationPolicy section to the reverse proxy configuration:

"users-route": {
  "ClusterId": "users-cluster",
  "AuthorizationPolicy": "authenticated",
  "Match": {
    "Path": "/users-service/{**catch-all}"
  },
  "Transforms": [
    { "PathPattern": "{**catch-all}" }
  ]
}

YARP will forward most credentials to the proxied services, such as cookies or bearer tokens because it might be important to identify the user in the individual microservices.

Adding Rate Limiting

You can also use an API gateway to introduce rate limiting to your system. It's a technique to limit the number of requests to your API to improve security and reduce the load on the servers.

You can learn more about how to use rate limiting in .NET here.

As you can already guess, YARP supports the native rate limiting mechanism added in .NET 7.

All you need to do is define a rate limit policy:

builder.Services.AddRateLimiter(rateLimiterOptions =>
{
    rateLimiterOptions.AddFixedWindowLimiter("fixed", options =>
    {
        options.Window = TimeSpan.FromSeconds(10);
        options.PermitLimit = 5;
    });
});

Then you need to call UseRateLimiter to add the rate limiter middleware to the request pipeline. It's important to do it before calling MapReverseProxy.

app.UseRateLimiter();

app.MapReverseProxy();

And then, you can apply rate limiting to the desired route using the RateLimiterPolicy section in the reverse proxy configuration:

"products-route": {
  "ClusterId": "products-cluster",
  "RateLimiterPolicy": "fixed",
  "Match": {
    "Path": "/products-service/{**catch-all}"
  },
  "Transforms": [
    { "PathPattern": "{**catch-all}" }
  ]
}

In Summary

An API gateway is a critical component for a robust microservices system implementation.

And YARP is an excellent option if you want to build an API gateway with .NET.

I created a sample API gateway implementation with YARP, which you can find here. The system consists of two APIs, and the API gateway is configured to route requests between them. It also implements:

In this newsletter, we only scratched the surface of what's possible with YARP.

Here are some useful resources if you want to learn more:

That's all for today.

Hope it was helpful.

Today's action step: Download the source code for the sample application implementing an API gateway with YARP, and take it for a spin. You can challenge yourself by creating multiple instances of a single service and configuring load balancing with the various load balancing algorithms.


Whenever you're ready, there are 4 ways I can help you:

  1. Modular Monolith Architecture (NEW): Join 350+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  2. Pragmatic Clean Architecture: Join 2,700+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.
  3. Patreon Community: Join a community of 1,050+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.
  4. Promote yourself to 48,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 48,000+ engineers who are improving their skills every Saturday morning.