Skip to main content

Getting Started

Installation​

Install the core packages. ZeroAlloc.Rest bundles the source generator, so no separate analyzer reference is needed:

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

The runtime packages target net8.0 and net10.0. Build with the .NET 10 SDK, whose compiler the source generator needs, whichever of the two your project targets. On net8.0, ZeroAlloc.Rest.SystemTextJson brings the System.Text.Json 10 package, so both targets serialize alike.

Or via <PackageReference>:

<PackageReference Include="ZeroAlloc.Rest" Version="x.y.z" />
<PackageReference Include="ZeroAlloc.Rest.DependencyInjection" Version="x.y.z" />
<PackageReference Include="ZeroAlloc.Rest.SystemTextJson" Version="x.y.z" />

ZeroAlloc.Rest.DependencyInjection is what generates the AddI{Interface} extension used below and adds Microsoft.Extensions.Http. Skip it if you build the client by hand instead; see Dependency Injection: Without dependency injection.

The standalone ZeroAlloc.Rest.Generator package is still published for backwards compatibility with existing direct PackageReferences, but new consumers should reference only ZeroAlloc.Rest.

Define an interface​

Decorate your interface with [ZeroAllocRestClient]. The source generator picks it up and emits a concrete implementation at compile time.

using ZeroAlloc.Rest.Attributes;

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

[Post("/users")]
Task<UserDto> CreateUserAsync([Body] CreateUserRequest request, CancellationToken ct = default);

[Delete("/users/{id}")]
Task DeleteUserAsync(int id, CancellationToken ct = default);
}

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

Register in ASP.NET Core or a generic host​

The generator also emits an AddI{InterfaceName} extension method:

// Program.cs
builder.Services.AddIUserApi(options =>
{
options.BaseAddress = new Uri("https://api.example.com");
options.UseSerializer<SystemTextJsonSerializer>();
});

This registers IUserApi as a typed HttpClient via IHttpClientFactory. The underlying HttpClient is managed by the factory's handler lifetime.

Use the client​

public class UserService(IUserApi api)
{
public async Task<UserDto> GetUserAsync(int id, CancellationToken ct = default)
=> await api.GetUserAsync(id, ct);
}

What the generator produces​

Given the interface above, the generator writes two files at compile time:

  • IUserApi.g.cs — UserApiClient : IUserApi with typed HTTP calls
  • IUserApi.DI.g.cs — AddIUserApi(IServiceCollection, Action<ZeroAllocClientOptions>) extension, emitted only when ZeroAlloc.Rest.DependencyInjection is referenced

Each file name starts with the interface's namespace and any containing types, for example MyApp.IUserApi.g.cs, so interfaces with the same name in different namespaces each get their own files. File names are not a contract and may change between releases.

You can inspect the generated code in Visual Studio via Analyzers → ZeroAlloc.Rest.Generator → Generated files.

Next steps​

  • Routing — path parameters and route templates
  • Parameters — query strings, request bodies, and headers
  • Serialization — plug in System.Text.Json, MemoryPack, or your own serializer