
Building a working API is one thing β building a fast and efficient API is another. As your application scales or handles more users, performance becomes a critical concern.
In this article, weβll walk through practical techniques to optimise ASP.NET Core APIs for speed and efficiency, covering compression, caching, efficient serialisation, memory pressure, streaming, and monitoring.
π 1. Enable Response Compression
ASP.NET Core does not enable compression by default. Enabling it can drastically reduce payload sizes β especially for JSON-heavy responses.
π Example: a 45KB JSON payload can be reduced to 8KB with Gzip compression.
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/json" });
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
π Response compression in ASP.NET Core
π§ 2. Use Caching Strategically
Caching reduces redundant work and improves throughput dramatically.
πΈ a) Response Caching
Response caching stores entire responses and serves them directly, saving CPU cycles and database hits.
builder.Services.AddResponseCaching();
app.UseResponseCaching();
[HttpGet]
[ResponseCache(Duration = 60)]
public IActionResult GetCachedData()
{
return Ok(new { message = "Cached response", time = DateTime.UtcNow });
}
πΈ b) In-Memory or Distributed Caching
Use IMemoryCache or IDistributedCache to cache database or computation-heavy results.
public class MyService
{
private readonly IMemoryCache _cache;
public MyService(IMemoryCache cache) => _cache = cache;
public async Task<string> GetDataAsync()
{
return await _cache.GetOrCreateAsync("data-key", entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
return FetchFromDatabaseAsync();
});
}
}
π§© 3. Use Efficient JSON Serialisation
ASP.NET Core uses System.Text.Json by default β itβs fast, but you can still tweak it for better results.
β Recommended options:
builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
β‘ Advanced: Source Generators
Improves serialisation speed and reduces allocations.
[JsonSerializable(typeof(MyResponseModel))]
internal partial class MyJsonContext : JsonSerializerContext { }
var json = JsonSerializer.Serialize(myData, MyJsonContext.Default.MyResponseModel);
π System.Text.Json overview
β»οΈ 4. Reduce Allocations and GC Pressure
Excessive allocations β frequent garbage collections β latency spikes. Use low-allocation patterns whenever possible.
β Tips:
-
Reuse buffers with
ArrayPool<T>:var buffer = ArrayPool<byte>.Shared.Rent(1024); // use buffer... ArrayPool<byte>.Shared.Return(buffer); -
Prefer
readonly structwhen the object is immutable and passed by reference. -
Avoid unnecessary
.ToList(),.Select()and LINQ operations in hot paths. -
Donβt return
IEnumerable<T>if the data is already materialised.
π Performance best practices for .NET
π 5. Stream Data When Possible
When returning large datasets, avoid loading the full result into memory.
β
Use IAsyncEnumerable<T>:
[HttpGet]
public async IAsyncEnumerable<MyItem> StreamItems()
{
await foreach (var item in _repository.GetItemsAsync())
{
yield return item;
}
}
This supports efficient streaming over HTTP/2, reducing memory pressure and improving time-to-first-byte.
βοΈ 6. Use Dependency Injection Efficiently
Registering your services properly avoids unnecessary instantiations and memory waste.
β Guidelines:
Singleton: for stateless and thread-safe services.Scoped: for per-request lifetime.- Avoid injecting large services if only used conditionally.
- Consider lazy loading via
Lazy<T>or factory methods.
services.AddSingleton<HttpClient>();
services.AddScoped<IMyService, MyService>();
π Dependency injection in ASP.NET Core
π 7. Measure and Monitor
Donβt optimise blindly. Always profile first.
β Tools:
BenchmarkDotNetβ for isolated performance testing.- Visual Studio Profiler or JetBrains dotTrace
- Runtime metrics with OpenTelemetry, Application Insights, or Prometheus.
β Performance Checklist
- Is response compression enabled?
- Are you using
System.Text.Jsonwith proper settings? - Are you caching expensive operations or results?
- Have you measured and reduced memory allocations?
- Are you streaming large data instead of loading all at once?
- Is your dependency injection properly scoped?
- Are you monitoring real performance metrics in production?
π Conclusion
Efficient APIs arenβt just about fast code β theyβre about thoughtful design, strategic trade-offs, and the right tools. These techniques help reduce latency, CPU load, and memory usage, making your APIs ready for real-world scale.
Optimise smartly. Profile often. And ship fast.