Skip to content

About

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.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

577 Commits

Folders and files

Repository files navigation

ZeroAlloc.Rest

NuGet Build codecov License: MIT AOT GitHub Sponsors

Describe your HTTP API as a C# interface and get a typed REST client generated at compile time: a Refit alternative with no reflection, NativeAOT-ready, and no allocation beyond what the same call written by hand costs.

Why ZeroAlloc.Rest

  • Allocates exactly what hand-written HttpClient code does. A generated GET of a small DTO allocates 1,688 B per call, the same as the request written by hand, and a test fails the build if it ever allocates more.
  • 1.1–2.0× faster than Refit with 1.1–1.35× less allocation per call, measured in the same run.
  • Works under NativeAOT and trimming. No reflection, no IL emit, no DynamicMethod; CI publishes a NativeAOT app and fails on any trim or AOT warning.
  • Mistakes surface at compile time. A route template that does not match the method's parameters, or a Result error type with no mapper, is a compiler diagnostic.

Install

dotnet add package ZeroAlloc.Rest
dotnet add package ZeroAlloc.Rest.DependencyInjection
dotnet add package ZeroAlloc.Rest.SystemTextJson

The source generator ships inside ZeroAlloc.Rest. The other two give you the IHttpClientFactory registration and the JSON serializer used below.

Quick start

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using ZeroAlloc.Rest.Attributes;
using ZeroAlloc.Rest.SystemTextJson;

var services = new ServiceCollection();
services.AddIUserApi(options => // generated from the interface name
{
    options.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
    options.UseSerializer(new SystemTextJsonSerializer(AppJsonContext.Default));
});

var api = services.BuildServiceProvider().GetRequiredService<IUserApi>();
var user = await api.GetUserAsync(1);
Console.WriteLine($"{user.Id}: {user.Name}"); // 1: Leanne Graham

[ZeroAllocRestClient]
public interface IUserApi
{
    [Get("/users/{id}")]
    Task<UserDto> GetUserAsync(int id, CancellationToken ct = default);
}

public sealed record UserDto(int Id, string Name);

[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
[JsonSerializable(typeof(UserDto))]
internal sealed partial class AppJsonContext : JsonSerializerContext;

The generator writes UserApiClient, a sealed class implementing IUserApi, at compile time. Inject IUserApi wherever you need it.

What you get

  • Every parameter kind: path segments including {**path} catch-alls, [Query], [Body] and [Header].
  • Errors as values: return Result<T, HttpError> from ZeroAlloc.Results and 4xx/5xx responses, network failures and timeouts come back as a failure instead of an exception, or map them to your own error type.
  • Pluggable serializers: System.Text.Json, MemoryPack, MessagePack, or your own, and a different one per method when an API mixes formats.
  • Streaming responses for large bodies, without the copy HttpClient makes by default.
  • IHttpClientFactory integration through a generated Add{Interface} extension, or construct the client by hand without DI.
  • Tracing and metrics on the BCL ActivitySource and Meter, ready for OpenTelemetry: Telemetry.
  • OpenAPI import: generate the interface, models and JSON context from a spec with the zeroalloc generate CLI or an MSBuild item: OpenAPI code generation.
  • Retry, timeout, circuit breaker and rate limiting with ZeroAlloc.Rest.Resilience and ZeroAlloc.Resilience: Resilience.

Performance

Against Refit 16.4, in-memory handler, no network I/O. .NET 10.0.13, BenchmarkDotNet v0.15.8. Allocations are exact; speed is a same-run ratio.

Call ZA vs Refit Refit alloc ZA alloc
GET 1.5× faster 2,056 B 1,584 B
GET with query parameter 2.0× faster 2,128 B 1,616 B
POST 1.3× faster 2,816 B 2,088 B

Refit scans attributes with reflection and invokes expression trees on every call; the generated client is plain code. Raw HttpClient rows, DELETE, Result<T>, serializer throughput and the per-call allocation breakdown: docs/benchmarks.md.

Learn more

About

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.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages