YARP .NET API Gateway Microsserviços Backend

API Gateway com YARP no .NET: roteamento, autenticação e rate limiting

Como implementar um API Gateway em .NET com YARP (Yet Another Reverse Proxy): roteamento dinâmico, autenticação JWT centralizada, rate limiting.

N
Neryx Digital Architects
2 de setembro de 2025
13 min de leitura
230 profissionais leram
Categoria: Arquitetura Público: Times de engenharia e produto Etapa: Aprendizado

Em arquiteturas de microsserviços, o API Gateway é o ponto de entrada único da sua plataforma. Ele centraliza autenticação, roteamento, rate limiting, logging e transformação de requests — evitando duplicar essas responsabilidades em cada serviço. YARP (Yet Another Reverse Proxy) é a solução da Microsoft para isso: um proxy reverso altamente configurável construído sobre ASP.NET Core.

Por que YARP em vez de Nginx ou API Gateway gerenciado?

YARP roda dentro do seu processo .NET. Isso significa: você usa C# e Injeção de Dependência para customizar qualquer comportamento, tem acesso ao mesmo ecossistema de middlewares do ASP.NET Core, pode adicionar lógica de negócio no gateway sem linguagens de configuração complexas, e deploya como qualquer outra aplicação .NET (Docker, Kubernetes, Azure App Service).

Setup básico

dotnet add package Yarp.ReverseProxy

// Program.cs — configuração mínima:
var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

app.MapReverseProxy();
app.Run();
// appsettings.json — rotas e clusters:
{
  "ReverseProxy": {
    "Routes": {
      "pedidos-route": {
        "ClusterId": "pedidos-cluster",
        "Match": {
          "Path": "/api/pedidos/{**catch-all}"
        }
      },
      "produtos-route": {
        "ClusterId": "produtos-cluster",
        "Match": {
          "Path": "/api/produtos/{**catch-all}",
          "Methods": ["GET", "POST", "PUT", "DELETE"]
        }
      },
      "usuarios-route": {
        "ClusterId": "usuarios-cluster",
        "Match": {
          "Path": "/api/usuarios/{**catch-all}"
        },
        "AuthorizationPolicy": "requer-autenticacao"
      }
    },
    "Clusters": {
      "pedidos-cluster": {
        "Destinations": {
          "pedidos-1": { "Address": "http://pedidos-service:8080/" },
          "pedidos-2": { "Address": "http://pedidos-service-2:8080/" }
        },
        "LoadBalancingPolicy": "RoundRobin"
      },
      "produtos-cluster": {
        "Destinations": {
          "produtos-1": { "Address": "http://produtos-service:8080/" }
        }
      },
      "usuarios-cluster": {
        "Destinations": {
          "usuarios-1": { "Address": "http://usuarios-service:8080/" }
        }
      }
    }
  }
}

Autenticação JWT centralizada no gateway

O gateway valida o token JWT uma vez — os microsserviços recebem a identidade já verificada via header, sem precisar repetir a validação:

// Program.cs — JWT + política de autorização:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = builder.Configuration["Auth:Authority"]; // ex: https://accounts.neryx.com.br
        options.Audience = "api-gateway";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
        };
    });

builder.Services.AddAuthorization(options => { options.AddPolicy(“requer-autenticacao”, policy => policy.RequireAuthenticatedUser());

options.AddPolicy("requer-admin", policy =>
    policy.RequireAuthenticatedUser()
          .RequireClaim("role", "admin"));

});

// No pipeline: app.UseAuthentication(); app.UseAuthorization(); app.MapReverseProxy(); // YARP vem APÓS autenticação

Transformações de request e response

YARP permite modificar headers, paths e o body dos requests antes de repassar para os serviços downstream:

// Transformações via código C# — mais flexível que configuração JSON:
builder.Services
    .AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(context =>
    {
        // Remove headers que não devem chegar nos microsserviços
        context.AddRequestHeaderRemove("X-Forwarded-For");
        context.AddRequestHeaderRemove("Cookie");
    // Adiciona o ID do usuário autenticado como header para os serviços downstream
    context.AddRequestTransform(async transformContext =>
    {
        var user = transformContext.HttpContext.User;
        if (user.Identity?.IsAuthenticated == true)
        {
            var userId = user.FindFirst("sub")?.Value;
            var userEmail = user.FindFirst("email")?.Value;
            var userRoles = string.Join(",", user.FindAll("role").Select(c => c.Value));

            transformContext.ProxyRequest.Headers.Remove("X-User-Id");
            transformContext.ProxyRequest.Headers.Remove("X-User-Email");
            transformContext.ProxyRequest.Headers.Remove("X-User-Roles");

            if (userId != null) transformContext.ProxyRequest.Headers.Add("X-User-Id", userId);
            if (userEmail != null) transformContext.ProxyRequest.Headers.Add("X-User-Email", userEmail);
            if (userRoles.Length > 0) transformContext.ProxyRequest.Headers.Add("X-User-Roles", userRoles);
        }
    });

    // Reescreve o path: /api/pedidos/123 → /pedidos/123 (remove prefixo /api)
    context.AddPathRemovePrefix("/api");
});

Rate Limiting integrado

Use o rate limiting nativo do ASP.NET Core (.NET 7+) com YARP para proteger os serviços downstream:

using System.Threading.RateLimiting;

// Rate limiting por IP e por usuário autenticado: builder.Services.AddRateLimiter(options => { // Política padrão: 100 requests/minuto por IP options.AddPolicy(“por-ip”, context => RateLimitPartition.GetFixedWindowLimiter( partitionKey: context.Connection.RemoteIpAddress?.ToString() ?? “unknown”, factory: _ => new FixedWindowRateLimiterOptions { PermitLimit = 100, Window = TimeSpan.FromMinutes(1), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 10 }));

// Política para usuários autenticados: 1.000 requests/minuto por usuário
options.AddPolicy("por-usuario", context =>
{
    var userId = context.User.FindFirst("sub")?.Value ?? context.Connection.RemoteIpAddress?.ToString();
    return RateLimitPartition.GetFixedWindowLimiter(
        partitionKey: $"user:{userId}",
        factory: _ => new FixedWindowRateLimiterOptions
        {
            PermitLimit = 1000,
            Window = TimeSpan.FromMinutes(1),
        });
});

// Retorno 429 com Retry-After header
options.OnRejected = async (context, ct) =>
{
    context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
    if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
        context.HttpContext.Response.Headers.RetryAfter =
            ((int)retryAfter.TotalSeconds).ToString();
    await context.HttpContext.Response.WriteAsync("Rate limit excedido. Tente novamente em breve.", ct);
};

});

// Aplicar no pipeline: app.UseRateLimiter();

// Configurar a política por rota no JSON: // “RateLimiterPolicy”: “por-usuario” ← adicione nas rotas protegidas

Health checks e circuit breaker

// Verificação de saúde dos serviços downstream:
builder.Services.AddHealthChecks()
    .AddUrlGroup(new Uri("http://pedidos-service:8080/health"), "pedidos")
    .AddUrlGroup(new Uri("http://produtos-service:8080/health"), "produtos")
    .AddUrlGroup(new Uri("http://usuarios-service:8080/health"), "usuarios");

app.MapHealthChecks(“/health”, new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse });

// Circuit breaker com Polly (pacote Microsoft.Extensions.Http.Resilience): builder.Services .AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection(“ReverseProxy”)) .AddTransforms(…) // Adiciona resiliência nas chamadas ao downstream .ConfigureHttpClient((context, handler) => { handler.MaxConnectionsPerServer = 100; });

// Para circuit breaker completo, configure no cluster: // “HttpClient”: { // “ActivityTimeout”: “00:00:30” // }

Roteamento dinâmico via banco de dados

Para plataformas multi-tenant ou sistemas com rotas configuráveis em tempo real, YARP suporta carregamento dinâmico de configuração sem restart:

// Implementar IProxyConfigProvider para carregar rotas do banco:
public class DatabaseProxyConfigProvider : IProxyConfigProvider
{
    private readonly IServiceProvider _services;
    private volatile DatabaseProxyConfig _config;
    private CancellationTokenSource _cts = new();
public DatabaseProxyConfigProvider(IServiceProvider services)
{
    _services = services;
    _config = LoadConfigFromDatabase();
}

public IProxyConfig GetConfig() => _config;

// Chame este método para forçar reload das rotas
public void Update()
{
    var oldCts = _cts;
    _cts = new CancellationTokenSource();
    _config = LoadConfigFromDatabase();
    oldCts.Cancel(); // sinaliza para o YARP recarregar
}

private DatabaseProxyConfig LoadConfigFromDatabase()
{
    using var scope = _services.CreateScope();
    var db = scope.ServiceProvider.GetRequiredService<GatewayDbContext>();

    var routes = db.GatewayRoutes
        .Where(r => r.Ativo)
        .Select(r => new RouteConfig
        {
            RouteId = r.RouteId,
            ClusterId = r.ClusterId,
            Match = new RouteMatch { Path = r.PathPattern }
        }).ToList();

    var clusters = db.GatewayClusters
        .Where(c => c.Ativo)
        .Include(c => c.Destinations)
        .Select(c => new ClusterConfig
        {
            ClusterId = c.ClusterId,
            Destinations = c.Destinations.ToDictionary(
                d => d.DestinationId,
                d => new DestinationConfig { Address = d.Address })
        }).ToList();

    return new DatabaseProxyConfig(routes, clusters, _cts.Token);
}

}

// Registrar: builder.Services.AddSingleton<DatabaseProxyConfigProvider>(); builder.Services.AddReverseProxy() .LoadFromMemory([], []); // base vazia, o provider vai sobrescrever

Logging e observabilidade no gateway

// Middleware de logging de requests no gateway:
app.Use(async (context, next) =>
{
    var sw = Stopwatch.StartNew();
    var requestId = Guid.NewGuid().ToString("N")[..8];
    context.Request.Headers["X-Request-Id"] = requestId;
    context.Response.Headers["X-Request-Id"] = requestId;
var logger = context.RequestServices.GetRequiredService&lt;ILogger&lt;Program&gt;&gt;();

try
{
    await next(context);
}
finally
{
    sw.Stop();
    logger.LogInformation(
        "Gateway: {Method} {Path} → {StatusCode} ({Duration}ms) RequestId={RequestId}",
        context.Request.Method,
        context.Request.Path,
        context.Response.StatusCode,
        sw.ElapsedMilliseconds,
        requestId);
}

});

Quando usar YARP vs soluções gerenciadas

YARP é a escolha certa quando você quer controle total sobre o comportamento do gateway com código C#, já tem expertise em .NET e não quer aprender outra linguagem de configuração, ou precisa de lógica de negócio no gateway (ex: roteamento baseado em tenant ID do JWT). AWS API Gateway e Azure API Management são melhores quando você precisa de funcionalidades gerenciadas prontas (throttling visual, developer portal, analytics) e quer menos código para manter.

Conclusão

YARP resolve o problema do API Gateway com a mesma linguagem e ferramentas que você já usa em .NET. Roteamento, autenticação centralizada, rate limiting e transformações de request cobrem os casos de uso mais comuns em produção. O diferencial é a extensibilidade: qualquer comportamento customizado é um middleware ASP.NET Core.

Se você está desenhando a arquitetura de microsserviços e precisa de um API Gateway robusto e performático, a Neryx tem experiência com YARP e soluções gerenciadas em produção. Consultoria inicial gratuita.

Leitura complementar:

Precisa desenhar a próxima fase com menos retrabalho?

Fazemos discovery técnico para mapear riscos, arquitetura-alvo e sequência de execução antes de investir pesado.

Solicitar Discovery

Newsletter

Receba artigos como este no seu e-mail

Conteúdo técnico sobre arquitetura de software, .NET, IA e gestão de produto. Sem spam.