What it is
Refit turns a C# interface into a REST client, generating the HTTP calls from attributes at build time with no reflection at runtime.
Declare an interface with HTTP attributes. Refit generates the implementation, handling serialisation, query strings and headers.
Installation
dotnet add package RefitGetting started
The smallest useful thing you can do with it, and what each part means.
csharp
public interface IBookApi
{
[Get("/books/{id}")]
Task<Book> GetBookAsync(int id, CancellationToken ct = default);
[Get("/books")]
Task<List<Book>> SearchAsync([Query] string title, [Query] int? year = null);
[Post("/books")]
Task<Book> CreateAsync([Body] CreateBookRequest request);
[Headers("Authorization: Bearer")]
[Delete("/books/{id}")]
Task DeleteAsync(int id, [Header("Authorization")] string token);
}
builder.Services
.AddRefitClient<IBookApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
.AddStandardResilienceHandler(); // Polly, for freeAdvanced usage
Where the library earns its place over a simpler alternative.
csharp
// Returning ApiResponse<T> avoids exceptions for expected failures.
[Get("/books/{id}")]
Task<ApiResponse<Book>> TryGetBookAsync(int id);
var response = await api.TryGetBookAsync(42);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.NotFound) return null;
logger.LogError("api error {Status} {Content}",
response.StatusCode, response.Error?.Content);
}
// Otherwise, non-2xx throws ApiException.
try { await api.GetBookAsync(42); }
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- ApiException on a 404
- Expected. Either catch it with a status filter, or return ApiResponse<T> and check IsSuccessStatusCode.
- Socket exhaustion under load
- A new HttpClient per call. Always register through HttpClientFactory.
Best practices
- Register via AddRefitClient so HttpClientFactory manages connection lifetimes.
- Use ApiResponse<T> for endpoints where non-2xx responses are expected.
- Accept a CancellationToken on every method and pass it through.
- Layer Polly resilience on the registration rather than inside the interface.
Background
Why it exists, and what it was reacting to.
Modelled on Square's Retrofit for Java, Refit replaced hand-written HttpClient wrappers with a declarative interface, and uses a source generator so there is no runtime cost.
