Skip to content

Repository files navigation

AutoHttpClient.Generator

NuGetNuGet DownloadsCILicense: MIT.NET 10 Ready

AutoHttpClient.Generator is an AOT-safe, compile-time typed HTTP client for .NET. Annotate an interface with [HttpClient], decorate methods with [Get], [Post], [Put], [Delete], or [Patch], and the generator emits a strongly-typed implementation plus DI registration at build time.

Why AutoHttpClient.Generator?

  • Compile-time generated clients — no dynamic proxy generation, no reflection-heavy dispatch layer
  • AOT-safe — generated C# calls HttpClient directly, so it works cleanly with native AOT scenarios
  • Minimal ceremony — plain interfaces plus attributes, no hand-written wrappers
  • DI-readyAddAutoHttpClients() registers every generated client for IServiceCollection
  • Strongly typed — route values, query parameters, headers, and JSON bodies all come from your method signature

Why not Refit or RestSharp?

  • Refit is ergonomic, but relies on runtime plumbing and generated proxy behavior that is less predictable in AOT-focused deployments
  • RestSharp is a runtime HTTP abstraction with reflection-oriented configuration rather than compile-time emitted clients
  • AutoHttpClient.Generator keeps everything as generated source in your build output: explicit, trim-friendly, and zero-reflection

Installation

dotnet add package AutoHttpClient.Generator

Then register the generated clients:

builder.Services.AddAutoHttpClients();

Quick start

usingAutoHttpClient;[HttpClient]publicinterfaceIOrdersApi{[Get("/api/orders/{id}")]Task<Order?>GetOrderAsync(intid,CancellationTokenct=default);[Get("/api/orders")]Task<List<Order>>GetOrdersAsync([Query("status")]string?status=null,CancellationTokenct=default);[Post("/api/orders")]Task<Order>CreateOrderAsync([Body]CreateOrderRequestrequest,CancellationTokenct=default);[Put("/api/orders/{id}")]Task<Order>UpdateOrderAsync(intid,[Body]UpdateOrderRequestrequest,CancellationTokenct=default);[Delete("/api/orders/{id}")]TaskDeleteOrderAsync(intid,CancellationTokenct=default);[Get("/api/orders/{id}/status")]Task<HttpResponseMessage>GetOrderStatusRawAsync(intid,CancellationTokenct=default);}

Register the generated implementation:

builder.Services.AddAutoHttpClients();

This emits an internal sealed client implementation and a DI registration similar to:

services.AddHttpClient<IOrdersApi,OrdersApiClient>();

Parameter attributes

AutoHttpClient.Generator classifies parameters using these rules:

Parameter styleBehavior
[Body]Serialized as JSON request content
[Query("name")]Added to the query string using the provided name
[Query] or unattributed non-route parameterAdded to the query string using the parameter name
[Header("X-Name")]Added as an HTTP header
Route parameterAny parameter whose name appears in the route template, e.g. {id}
CancellationTokenPassed through to HttpClient and JSON helpers

Examples

[Get("/api/orders")]Task<List<Order>>GetOrdersAsync([Query("status")]string?status=null,intpage=1,CancellationTokenct=default);[Post("/api/orders")]Task<Order>CreateOrderAsync([Body]CreateOrderRequestrequest,[Header("X-Tenant")]stringtenant,CancellationTokenct=default);[Get("/api/orders/{id}")]Task<Order?>GetOrderAsync(intid,CancellationTokenct=default);

Return types

Return typeGenerated behavior
TaskSends the request and calls EnsureSuccessStatusCode()
Task<T>Sends the request, ensures success, and deserializes JSON with ReadFromJsonAsync<T>()
Task<T?>Same as Task<T> but preserves nullable result types
Task<HttpResponseMessage>Returns the raw response without EnsureSuccessStatusCode()

BaseAddress configuration

You can configure a base address directly on the interface attribute:

usingAutoHttpClient;[HttpClient(BaseAddress="https://api.example.com")]publicinterfaceIOrdersApi{[Get("/api/orders")]Task<List<Order>>GetOrdersAsync(CancellationTokenct=default);}

The generated DI registration configures the typed client:

services.AddHttpClient<IOrdersApi,OrdersApiClient>(client =>{client.BaseAddress=newUri("https://api.example.com");});

Scaffolding from OpenAPI/Swagger

AutoHttpClient.Generator now includes a small repo-side scaffolding tool for converting an OpenAPI/Swagger JSON document into a partial interface decorated with AutoHttpClient attributes.

Run it with:

dotnet run --project tools/AutoHttpClient.OpenApiScaffold -- --input swagger.json --output IMyApiClient.g.cs --namespace MyApp.Clients --interface-name IMyApiClient

The generated file is a one-time scaffold that you add to your project, then the existing AutoHttpClient.Generator source generator consumes it normally.

What it generates

  • [HttpClient] or [HttpClient(BaseAddress = "...")] when the spec declares a simple server URL
  • [Get], [Post], [Put], [Delete], [Patch] based on each OpenAPI operation
  • Route parameters as normal method parameters
  • Query parameters as [Query("name")]
  • Request bodies as [Body]
  • Task<T> return types using referenced schema names where possible

Current scope / limitations

  • Optimized for common OpenAPI 3 JSON documents
  • Swagger/OpenAPI 2 documents may work for basic paths/operations, but v3 is the primary target
  • Best support is for JSON request/response bodies with named schemas, simple path/query/header parameters, and standard HTTP verbs
  • Inline/anonymous schemas fall back to JsonElement (or collections/dictionaries of known types where possible)
  • Advanced OpenAPI features such as oneOf, anyOf, callbacks, multipart form uploads, and full DTO generation are not scaffolded yet
  • Named schemas are used as C# type names in the generated interface; you still need matching DTO types in your project

Comparison

FeatureAutoHttpClient.GeneratorRefitRestSharp
Compile-time generated clientPartial/runtime proxy behavior
AOT-safe⚠️ depends on runtime behavior
Zero reflection dispatch⚠️
Native HttpClient typed client DI⚠️ manual
Interface-first API
OpenAPI/Swagger scaffolding tool✅ (repo tool)⚠️ varies
Build-time diagnosticsLimited

Diagnostics

CodeSeverityMessage
AH001WarningMethod on a [HttpClient] interface has no HTTP method attribute and will not be generated.
AH002WarningRoute template parameter has no matching method parameter.
AH003ErrorMethod has multiple [Body] parameters; only one is allowed.

Generated attributes

The package emits these attributes at post-initialization time:

  • HttpClientAttribute
  • GetAttribute
  • PostAttribute
  • PutAttribute
  • DeleteAttribute
  • PatchAttribute
  • BodyAttribute
  • QueryAttribute
  • HeaderAttribute

Migrating from Refit

AutoHttpClient.Generator uses the same interface-first approach as Refit. Migration is mostly a find-and-replace of attributes.

1. Install and remove Refit

dotnet add package AutoHttpClient.Generator
dotnet remove package Refit
dotnet remove package Refit.HttpClientFactory

2. Replace Refit attributes with AutoHttpClient attributes

// Before (Refit)usingRefit;publicinterfaceIOrdersApi{[Get("/api/orders/{id}")]Task<Order?>GetOrderAsync(intid,CancellationTokenct=default);[Post("/api/orders")]Task<Order>CreateOrderAsync([Body]CreateOrderRequestrequest,CancellationTokenct=default);[Get("/api/orders")]Task<List<Order>>GetOrdersAsync([AliasAs("status")]string?status=null);}// After (AutoHttpClient.Generator)usingAutoHttpClient;[HttpClient]publicinterfaceIOrdersApi{[Get("/api/orders/{id}")]Task<Order?>GetOrderAsync(intid,CancellationTokenct=default);[Post("/api/orders")]Task<Order>CreateOrderAsync([Body]CreateOrderRequestrequest,CancellationTokenct=default);[Get("/api/orders")]Task<List<Order>>GetOrdersAsync([Query("status")]string?status=null);}

3. Update DI registration

// Before (Refit)builder.Services.AddRefitClient<IOrdersApi>().ConfigureHttpClient(c =>c.BaseAddress=newUri("https://api.example.com"));// After (AutoHttpClient.Generator)[HttpClient(BaseAddress="https://api.example.com")]publicinterfaceIOrdersApi{ ...}builder.Services.AddAutoHttpClients();

Attribute mapping

RefitAutoHttpClient.Generator
[Get("/path")][Get("/path")]
[Post("/path")][Post("/path")]
[Put("/path")][Put("/path")]
[Delete("/path")][Delete("/path")]
[Patch("/path")][Patch("/path")]
[Body][Body]
[AliasAs("name")][Query("name")]
[Header("X-Name")][Header("X-Name")]
[HeaderCollection]Not supported
[Authorize]Use [Header("Authorization")]

What Refit supports that AutoHttpClient.Generator doesn't (yet)

  • [HeaderCollection] dictionary headers
  • [Multipart] / [AttachmentName] for multipart form uploads
  • IObservable<T> return types
  • Custom JsonSerializerSettings per method

For projects using any of these heavily, hold off on migrating until support lands.

Also by the same author

🌐 Full suite overview: swevo.github.io

PackageDescription
AutoLog.GeneratorCompile-time high-performance logging — [Log(Level, Message)] on a partial method generates LoggerMessage.Define. AOT-safe.
AutoDispatch.GeneratorCompile-time CQRS dispatcher — [Handler] generates a strongly-typed IDispatcher. No MediatR, no reflection.
AutoWireCompile-time DI auto-registration — [Scoped]/[Singleton]/[Transient] generates IServiceCollection registration code.
AutoMap.GeneratorCompile-time object mapping with generated extension methods. AOT-safe AutoMapper alternative.
AutoValidate.GeneratorCompile-time FluentValidation wiring — discovers validators and generates AddValidators().
AutoResult.GeneratorCompile-time Result<T>[TryWrap] generates Try*() wrappers for every public method.
AutoQuery.GeneratorCompile-time LINQ query specs — [QuerySpec] generates a strongly-typed Apply(IQueryable<T>).

Related Packages

PackageDownloadsDescription
AutoWireDownloadsCompile-time dependency injection auto-registration for
AutoMap.GeneratorDownloadsCompile-time object mapping for
AutoQuery.GeneratorDownloadsCompile-time query composition for IQueryable using Roslyn incremental source generators
AutoArchitectureDownloadsCompile-time architecture/dependency-rule enforcement for
AutoDispatch.GeneratorDownloadsCompile-time CQRS dispatcher for
AutoLog.GeneratorDownloadsCompile-time high-performance logging for
AutoValidate.GeneratorDownloadsCompile-time FluentValidation wiring for

License

MIT

About

Compile-time typed HTTP client generation for .NET — [HttpClient] on an interface generates a strongly-typed HttpClient implementation at build time. AOT-safe Refit alternative.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages