Ocelot API Gateway + .NET: production patterns that work

Sep 12, 2025
ocelotapi-gateway.netmicroservices
0

Ocelot is a lightweight, code-first API Gateway for .NET. It handles routing, aggregation, auth, rate limiting, caching, and resiliency in front of your downstream services.

When to use Ocelot

  • You’re on .NET and want a simple gateway without external infra
  • You need service aggregation, token validation, and per-route policies
  • You prefer configuration as code and easy containerization

If you need L7/WAF, global edge, or WebSockets at scale, consider Azure Front Door + YARP, NGINX, or Envoy. Ocelot is great for single-region app/API gateways.

Quick start

dotnet new web -n Gateway
cd Gateway
dotnet add package Ocelot

Program.cs:

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();
var app = builder.Build();
await app.UseOcelot();
app.Run();

ocelot.json minimal example:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/users/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [{ "Host": "users.api", "Port": 443 }],
      "UpstreamPathTemplate": "/users/{everything}",
      "UpstreamHttpMethod": ["GET", "POST", "PUT", "DELETE"]
    }
  ],
  "GlobalConfiguration": {}
}

Auth with JWT (Azure AD / Auth0 / Cognito)

Validate tokens at the gateway, forward user claims to services.

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/orders/{everything}",
      "UpstreamHttpMethod": ["GET", "POST"],
      "DownstreamPathTemplate": "/api/orders/{everything}",
      "DownstreamHostAndPorts": [{ "Host": "orders", "Port": 8080 }],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "JwtScheme",
        "AllowedScopes": ["orders.read", "orders.write"]
      },
      "AddHeadersToDownstream": {
        "X-User-Id": "{Claims[oid]}"
      }
    }
  ]
}

Program.cs (auth setup):

builder.Services
  .AddAuthentication()
  .AddJwtBearer("JwtScheme", options =>
  {
      options.Authority = builder.Configuration["Auth:Authority"]; // e.g. https://login.microsoftonline.com/tenant
      options.Audience  = builder.Configuration["Auth:Audience"];  // api://gateway
  });

Resilience & QoS

Enable retries, timeouts, circuit breakers per route.

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/inventory/{everything}",
      "DownstreamPathTemplate": "/api/inventory/{everything}",
      "DownstreamHostAndPorts": [{ "Host": "inventory", "Port": 8081 }],
      "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 5000, "TimeoutValue": 4000 },
      "LoadBalancerOptions": { "Type": "RoundRobin" }
    }
  ]
}

Aggregation (single call → multiple services)

Use Aggregates to compose responses.

{
  "Routes": [
    { "UpstreamPathTemplate": "/profile/{userId}", "DownstreamPathTemplate": "/api/users/{userId}", "Key": "user" },
    { "UpstreamPathTemplate": "/profile/{userId}/orders", "DownstreamPathTemplate": "/api/orders/by-user/{userId}", "Key": "orders" }
  ],
  "Aggregates": [
    {
      "UpstreamPathTemplate": "/profile-aggregate/{userId}",
      "RouteKeys": ["user", "orders"],
      "Aggregator": "FakeDefinedAggregator" // or custom aggregator class
    }
  ]
}

Custom aggregator example:

public class ProfileAggregator : IDefinedAggregator
{
    public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
    {
        var user = await responses[0].Items.DownstreamResponse().Content.ReadAsStringAsync();
        var orders = await responses[1].Items.DownstreamResponse().Content.ReadAsStringAsync();
        var json = $"{{\"user\":{user},\"orders\":{orders}}}";
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        return new DownstreamResponse(content, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
    }
}

Register it:

builder.Services.AddOcelot().AddSingletonDefinedAggregator<ProfileAggregator>();

Rate limiting & caching

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/public/{everything}",
      "DownstreamPathTemplate": "/api/public/{everything}",
      "RateLimitOptions": { "EnableRateLimiting": true, "Period": "1m", "PeriodTimespan": 60, "Limit": 120 },
      "FileCacheOptions": { "TtlSeconds": 30, "Region": "public-cache" }
    }
  ],
  "GlobalConfiguration": { "RateLimitOptions": { "DisableRateLimitHeaders": false } }
}
``]

## Deployment notes

- Package as a container; mount `ocelot.*.json` via config map/volume for hot reloads.
- Use health checks that probe downstreams and expose `/health`.
- Keep gateway stateless; scale horizontally behind a load balancer.

## Checklist

- [ ] Per‑route auth scopes and headers
- [ ] Timeouts, retries, and circuit breakers
- [ ] Aggregation where it adds value only
- [ ] Global + per‑route rate limits
- [ ] Access logs and correlation IDs
- [ ] Config as code with environment overrides

With Ocelot, you can stand up a capable gateway in hours, keep policies versioned with code, and evolve routes safely as your .NET services grow.