Skip to content

Output Caching for .NET Minimal APIs

Granit.Http.OutputCaching wraps ASP.NET Core’s native OutputCaching middleware with Granit defaults: authenticated responses are excluded (GDPR), cache keys automatically vary by tenant ID when multi-tenancy is active, and tag-based eviction lets modules invalidate their own cached responses.

The in-memory store works out of the box. For distributed caching across pods, install Granit.Http.OutputCaching.StackExchangeRedis.

[DependsOn(typeof(GranitHttpOutputCachingModule))]
public class AppModule : GranitModule { }
{
"OutputCaching": {
"DefaultExpiration": "00:01:00"
}
}
app.UseRouting();
app.UseCors(); // Granit.Http.Cors
app.UseGranitOutputCaching(); // ← AFTER CORS, AFTER routing
app.UseAuthorization();

The base policy automatically applied to every cached endpoint:

RuleDefaultRationale
Authenticated responses excludedtruePrevents personal data leaks across users
Set-Cookie responses excludedtrueSession tokens must never be shared
Default TTL60 sSafe for most read-heavy APIs
VaryByQuery keyspage, pageSize, sort, filter, q, include, expandCovers pagination, search, and resource expansion

When Granit.MultiTenancy is active, cache keys automatically vary by ICurrentTenant.Id:

  • Tenant A’s cached response is never served to Tenant B
  • Each cached response is tagged tenant:{id} for per-tenant bulk eviction
  • Without multi-tenancy, the policy is a no-op (zero overhead)

Use .CacheOutput() on minimal API endpoints:

app.MapGet("/api/v1/reference-data", () => GetReferenceData())
.CacheOutput(p => p.Tag("reference-data"));
app.MapGet("/api/v1/products/{id}", (int id) => GetProduct(id))
.CacheOutput();
PolicyBehavior
GranitDefaultGDPR + tenant-aware + configured TTL
GranitNoCacheExplicitly disables caching for an endpoint
app.MapGet("/api/v1/health", () => "OK")
.CacheOutput(GranitOutputCachePolicyNames.NoCache);

Inject IOutputCacheEvictionService to invalidate cached responses:

public sealed class UpdateProductHandler(IOutputCacheEvictionService eviction)
{
public async Task HandleAsync(UpdateProductCommand cmd, CancellationToken ct)
{
// ... update product ...
// Evict all cached responses tagged "products"
await eviction.EvictModuleCacheAsync("products", ct);
}
}
// Evict all cached responses for a specific tenant
await eviction.EvictTenantCacheAsync(tenantId, ct);

Granit.Http.OutputCaching.StackExchangeRedis replaces the in-memory store with Redis for cross-pod cache sharing.

builder.Services.AddHealthChecks()
.AddGranitRedisOutputCacheHealthCheck();

Returns Healthy, Degraded (latency > 100 ms), or Unhealthy (unreachable). Tagged readiness and startup for Kubernetes probes.

PropertyDefaultDescription
OutputCaching:DefaultExpiration00:01:00Default response cache TTL
OutputCaching:VaryByQueryKeys["page","pageSize","sort","filter","q","include","expand"]Query params included in cache key
OutputCaching:EnableTenantIsolationtrueVary by tenant ID when available
OutputCaching:ExcludeAuthenticatedResponsestrueSkip caching for authenticated users
OutputCaching:Redis:IsEnabledtrueEnable/disable Redis store
OutputCaching:Redis:Configurationlocalhost:6379Redis connection string
OutputCaching:Redis:InstanceNamedd:oc:Redis key prefix
OutputCaching:Redis:RequireTlstrueEnforce TLS for Redis
CategoryKey typesPackage
ModuleGranitHttpOutputCachingModuleGranit.Http.OutputCaching
OptionsOutputCachingOptionsGranit.Http.OutputCaching
PoliciesGranitOutputCachePolicyNames, GdprCompliantOutputCachePolicy, TenantAwareOutputCachePolicyGranit.Http.OutputCaching
EvictionIOutputCacheEvictionServiceGranit.Http.OutputCaching
ExtensionsAddGranitOutputCaching(), UseGranitOutputCaching()Granit.Http.OutputCaching
Redis moduleGranitHttpOutputCachingStackExchangeRedisModuleGranit.Http.OutputCaching.StackExchangeRedis
Redis optionsRedisOutputCachingOptionsGranit.Http.OutputCaching.StackExchangeRedis
Redis extensionsAddGranitRedisOutputCache(), AddGranitRedisOutputCacheHealthCheck()Granit.Http.OutputCaching.StackExchangeRedis
  • GDPR Article 5(1)(f) — integrity and confidentiality: authenticated responses excluded from caching by default
  • ISO 27001 A.10.1 — encryption in transit: Redis TLS enforced by default
  • ISO 27001 A.9.4 — access control: tenant-isolated cache keys prevent cross-tenant data leakage