API Gateway in Microservices C#: A Practical Guide
An API gateway in a C# microservices architecture is a single entry point that sits between clients and your backend services, routing requests, handling cross-cutting concerns like authentication and rate limiting, and shielding consumers from the internal structure of your system. Instead of a mobile app or frontend calling orders-service, inventory-service, and payments-service directly, it calls one gateway, which forwards each request to the right internal service.
In .NET, this pattern is usually implemented with a dedicated ASP.NET Core project using a library like Ocelot or Microsoft's own YARP (Yet Another Reverse Proxy), rather than hand-rolling routing logic. This article covers how the gateway pattern works in a C# stack, what it actually solves, and how to decide whether you need one.
Why Microservices Need a Gateway
When you split a monolith into services, clients suddenly need to know about every service's address, port, and API shape. That creates problems:
- Chatty clients — a single screen might need data from five services, meaning five round trips from a mobile device on a weak connection.
- Duplicated cross-cutting logic — auth, logging, rate limiting, and CORS get implemented separately in each service.
- Leaky internal topology — clients become coupled to internal service boundaries, making refactoring painful.
- Inconsistent security surface — every service ends up exposed directly to the internet unless something centralizes that.
An API gateway solves this by becoming the only publicly reachable component. Internal services stay on a private network, and the gateway handles translation, aggregation, and policy enforcement.
Core Responsibilities of a Gateway in a C# Stack
A typical ASP.NET Core gateway handles:
- Routing — mapping public paths like
/api/orders/*to internal services (http://orders-svc:5001). - Authentication and authorization — validating JWTs or API keys once, at the edge, instead of in every service.
- Rate limiting and throttling — using ASP.NET Core's built-in
Microsoft.AspNetCore.RateLimitingmiddleware. - Request aggregation — combining calls to multiple downstream services into one response for the client.
- Response caching — reducing load on downstream services for read-heavy endpoints.
- Load balancing — distributing traffic across multiple instances of a service.
- Observability — centralized logging, correlation IDs, and metrics for every request that enters the system.
Building a Minimal Gateway with YARP
YARP is Microsoft's reverse proxy toolkit, built for exactly this use case, and it's configuration-driven, which keeps the code small.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
And the routing table in appsettings.json:
{
"ReverseProxy": {
"Routes": {
"orders-route": {
"ClusterId": "orders-cluster",
"Match": { "Path": "/api/orders/{**catch-all}" }
}
},
"Clusters": {
"orders-cluster": {
"Destinations": {
"d1": { "Address": "http://orders-service:5001/" }
}
}
}
}
}
This is enough to route incoming requests to the orders service without writing manual proxy code. Add authentication middleware, rate limiting policies, or custom transforms as your requirements grow.
Ocelot as the Alternative
Ocelot is an older, more opinionated .NET gateway library, popular because it's entirely JSON-configuration based and includes built-in support for authentication, caching, and request aggregation without writing custom C# code:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/orders/{id}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [{ "Host": "orders-service", "Port": 5001 }],
"UpstreamPathTemplate": "/orders/{id}",
"UpstreamHttpMethod": ["GET"]
}
]
}
YARP tends to be preferred for new projects because it's actively maintained by Microsoft and integrates more naturally with ASP.NET Core's middleware pipeline, but Ocelot's aggregation features can save time if you need to combine multiple downstream calls into one response.
When You Don't Need a Custom Gateway
Not every C# system needs a hand-built gateway service. If you're consuming a third-party API rather than exposing your own microservices, running your own gateway is often overkill — you're managing routing infrastructure for a single upstream provider. In that case a managed API layer is usually simpler.
This is the same underlying pattern SubToAPI applies, just pointed at Claude instead of your own services: it puts a stable HTTPS gateway in front of your Claude access, issuing application-scoped sub_live_... keys, handling streaming and tool use, and giving you usage metadata and team seats in one dashboard, so you're not writing and maintaining proxy code for a single external dependency. If you're building a C# app that needs to call Claude through a clean API rather than managing gateway infrastructure yourself, check the quickstart or pricing.
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SUBTOAPI_KEY"));
var response = await client.PostAsJsonAsync("https://api.subtoapi.app/v1/messages", new
{
model = "claude-sonnet-4",
max_tokens = 1024,
messages = new[] { new { role = "user", content = "Summarize this order log." } }
});
Trade-offs to Consider
A gateway adds a network hop and a new point of failure. If it goes down, everything behind it becomes unreachable, so it needs to be deployed with redundancy, health checks, and proper timeouts. It also risks becoming a bottleneck if you push too much business logic into it — aggregation and auth are reasonable; domain logic is not. Keep the gateway thin: routing, security, and cross-cutting concerns only.
For smaller systems with two or three services, a gateway can also be premature. Direct client-to-service calls with a shared auth library might be simpler until the number of services and consumers grows enough to justify centralizing the entry point.
Questions
Is an API gateway the same as a load balancer in .NET? No. A load balancer distributes traffic across instances of the same service; a gateway routes to different services, applies auth, and can aggregate responses. Gateways often include load balancing as one feature among several.
Should I use YARP or Ocelot for a new C# project? YARP is generally the better default for new ASP.NET Core projects since it's maintained by Microsoft and integrates directly with the middleware pipeline. Choose Ocelot if you specifically need its built-in request aggregation without writing custom transform code.
Does every microservices architecture need a gateway? No. Small systems with few services and internal consumers can skip it. A gateway earns its complexity once you have multiple public clients, need centralized auth, or want to hide internal service topology from consumers.