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 : IUserApiwith typed HTTP callsIUserApi.DI.g.cs—AddIUserApi(IServiceCollection, Action<ZeroAllocClientOptions>)extension, emitted only whenZeroAlloc.Rest.DependencyInjectionis 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