ZeroAlloc.Rest
1.1.3
dotnet add package ZeroAlloc.Rest --version 1.1.3
NuGet\Install-Package ZeroAlloc.Rest -Version 1.1.3
<PackageReference Include="ZeroAlloc.Rest" Version="1.1.3" />
<PackageVersion Include="ZeroAlloc.Rest" Version="1.1.3" />
<PackageReference Include="ZeroAlloc.Rest" />
paket add ZeroAlloc.Rest --version 1.1.3
#r "nuget: ZeroAlloc.Rest, 1.1.3"
#:package ZeroAlloc.Rest@1.1.3
#addin nuget:?package=ZeroAlloc.Rest&version=1.1.3
#tool nuget:?package=ZeroAlloc.Rest&version=1.1.3
ZeroAlloc.Rest
ZeroAlloc.Rest is a source-generated, Native AOT-compatible REST client for .NET 10+. Define your HTTP API as a C# interface — the Roslyn generator emits a fully type-safe, zero-reflection implementation at compile time. No runtime code generation, no IL emit, no allocations beyond the HTTP layer itself.
Install
The source generator is bundled into the main package — a single PackageReference is all you need:
dotnet add package ZeroAlloc.Rest
dotnet add package ZeroAlloc.Rest.SystemTextJson
Or via <PackageReference>:
<PackageReference Include="ZeroAlloc.Rest" Version="x.y.z" />
<PackageReference Include="ZeroAlloc.Rest.SystemTextJson" Version="x.y.z" />
The standalone
ZeroAlloc.Rest.Generatorpackage is still published for backwards compatibility with existing direct PackageReferences, but new consumers should reference onlyZeroAlloc.Rest.
Optional: resilience policies
To add retry, timeout, and circuit-breaker behaviour to a client, install the bridge package alongside ZeroAlloc.Resilience:
dotnet add package ZeroAlloc.Rest.Resilience
dotnet add package ZeroAlloc.Resilience
Quick Start
1. Define your API interface:
using ZeroAlloc.Rest.Attributes;
[ZeroAllocRestClient]
public interface IUserApi
{
[Get("/users/{id}")]
Task<UserDto> GetUserAsync(int id, CancellationToken ct = default);
[Get("/users")]
Task<List<UserDto>> ListUsersAsync([Query] string? name = null, CancellationToken ct = default);
[Post("/users")]
Task<UserDto> CreateUserAsync([Body] CreateUserRequest request, CancellationToken ct = default);
[Delete("/users/{id}")]
Task DeleteUserAsync(int id, CancellationToken ct = default);
}
2. Register in DI:
builder.Services.AddIUserApi(options =>
{
options.BaseAddress = new Uri("https://api.example.com");
options.UseSerializer<SystemTextJsonSerializer>();
});
3. Inject and use:
public class UserService(IUserApi api)
{
public Task<UserDto> GetAsync(int id) => api.GetUserAsync(id);
}
The generator produces UserApiClient — a sealed class implementing IUserApi — at compile time. No reflection, no proxies, no DynamicMethod.
Performance
Measured on .NET 10.0.7, i9-12900HK, BenchmarkDotNet v0.15.8. In-memory handler; no real network I/O. See docs/benchmarks.md for methodology and full results.
| Method | Mean | vs Refit | Allocated |
|---|---|---|---|
| Raw HttpClient (GET baseline) | 2.09 μs | — | 1.38 KB |
| ZeroAlloc.Rest GET | 3.52 μs | 3.6× faster | 1.88 KB |
| Refit GET | 12.70 μs | 1× | 2.88 KB |
| ZeroAlloc.Rest POST | 6.70 μs | 1.7× faster | 2.64 KB |
| Refit POST | 11.62 μs | 1× | 3.46 KB |
| ZeroAlloc.Rest QueryParam | 4.28 μs | 3.6× faster | 1.99 KB |
| Refit QueryParam | 15.51 μs | 1× | 3.55 KB |
| ZeroAlloc.Rest DELETE | 1.92 μs | 2.4× faster | 1.61 KB |
| Refit DELETE | 4.62 μs | 1× | 2.45 KB |
ZA is 1.7–3.6× faster than Refit across every call shape with 1.3–1.5× less allocation. Refit pays for reflection-based attribute scanning + expression-tree invocation on every call; ZA's generated client lives within 1.7–3.2× of the raw HttpClient floor.
Features
- Source-generated — zero runtime reflection; compile-time type safety
- Native AOT compatible — no
DynamicMethod, no IL emit, noType.GetType - Per-method serializer override —
[Serializer(typeof(MySerializer))]for mixed protocols - Path, query, body, and header parameters —
{id},[Query],[Body],[Header("X-Api-Key")] Result<T, HttpError>— typed success/error returns viaZeroAlloc.Results; no exception-throwing on 4xx/5xx- OpenAPI code generation —
OpenApiInterfaceGeneratorAPI + MSBuild<ZeroAllocApiSpec>task - Pluggable serializers — System.Text.Json, MemoryPack, MessagePack, or bring your own
- IHttpClientFactory integration —
AddI{Interface}generated extension method - Resilience bridge —
ZeroAlloc.Rest.Resiliencewraps any client with[Retry],[Timeout],[CircuitBreaker], and[RateLimit]viaAddRestResilience<,,>()
Telemetry
Every generated client emits OpenTelemetry-compatible signals out of the box, with no extra dependency beyond the BCL System.Diagnostics primitives.
Tracing. An ActivitySource("ZeroAlloc.Rest") produces one span per call, named Interface.Method. Tags: http.method, http.status_code, server.address, rest.method. Exceptions set ActivityStatusCode.Error with the message.
Metrics. The generator emits rest.requests_total (counter) and rest.request_duration_ms (histogram) under the ZeroAlloc.Rest Meter. Tags: http.method, http.status_code, server.address, rest.method (the generated Interface.Method identifier).
The counter is incremented only on the success path. On exception, the histogram records the elapsed duration but the counter is left untouched — this lets operators distinguish failed attempts via histogram count vs. counter delta. The exception-path histogram carries only http.method and rest.method, since http.status_code and server.address may not be known.
Subscription:
services.AddOpenTelemetry()
.WithTracing(t => t.AddSource("ZeroAlloc.Rest"))
.WithMetrics(m => m.AddMeter("ZeroAlloc.Rest"));
Documentation
| Page | Description |
|---|---|
| Getting Started | Install, register, and make your first call |
| Routing | Route templates and path parameters |
| Parameters | Query, body, header, and path parameters |
| Serialization | Built-in serializers and custom IRestSerializer |
| Dependency Injection | Generated DI extension and IHttpClientFactory |
| Native AOT | AOT safety guarantees and publish configuration |
| OpenAPI Code Generation | Generate interfaces from OpenAPI specs |
| Benchmarks | Performance comparison vs Refit and raw HttpClient |
| Testing | Testing patterns with WireMock.Net |
| Advanced | Result<T, HttpError>, multiple serializers, edge cases |
| Resilience | Retry, timeout, circuit-breaker, and rate-limit via ZeroAlloc.Rest.Resilience |
| Cookbook | End-to-end recipes |
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Http (>= 10.0.8)
- ZeroAlloc.Collections (>= 0.1.3)
- ZeroAlloc.Results (>= 0.1.4)
- ZeroAlloc.Serialisation (>= 1.0.0)
- ZeroAlloc.ValueObjects (>= 1.2.0)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on ZeroAlloc.Rest:
| Package | Downloads |
|---|---|
|
ZeroAlloc.Rest.SystemTextJson
Source-generated, AOT-compatible REST client for .NET |
|
|
ZeroAlloc.Rest.Resilience
Wires ZeroAlloc.Rest clients through ZeroAlloc.Resilience proxies. Annotate a [ZeroAllocRestClient] interface with [Retry]/[Timeout]/[CircuitBreaker]; call AddRestResilience to register the resilience-wrapped HTTP client. |
|
|
ZeroAlloc.Rest.MemoryPack
Source-generated, AOT-compatible REST client for .NET |
|
|
ZeroAlloc.Rest.MessagePack
Source-generated, AOT-compatible REST client for .NET |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.3 | 328 | 5/13/2026 |
| 1.1.2 | 264 | 5/12/2026 |
| 1.1.1 | 778 | 5/2/2026 |
| 1.1.0 | 133 | 5/1/2026 |
| 1.0.2 | 114 | 4/29/2026 |
| 1.0.1 | 137 | 4/29/2026 |
| 1.0.0 | 119 | 4/28/2026 |
| 0.2.0 | 140 | 4/14/2026 |
| 0.1.5 | 117 | 4/25/2026 |
| 0.1.4 | 118 | 4/25/2026 |
| 0.1.3 | 120 | 4/14/2026 |
| 0.1.2 | 134 | 4/2/2026 |
| 0.1.1 | 123 | 4/1/2026 |
| 0.1.0 | 130 | 3/31/2026 |