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<ILogger<Program>>(); 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: