Refit in .NET: Solving the Problem of API Boilerplate with Real-Life Comparisons
Comparing API Calls with and Without Refit
When building .NET applications, consuming REST APIs is a common task. However, the traditional approach with HttpClient
can quickly lead to repetitive, error-prone code. Refit simplifies this by abstracting much of the boilerplate and providing a clean, declarative way to define and consume APIs.
Let’s explore the differences by comparing API calls with and without Refit, highlighting the problems Refit solves.
📌Explore more at: https://dotnet-fullstack-dev.blogspot.com/
🌟 Clapping would be appreciated! 🚀
Scenario: Fetching Weather Data from OpenWeatherMap
We’ll build a simple .NET application to fetch weather data for a given city using the OpenWeatherMap API.
Without Refit: Traditional HttpClient Approach
1. Boilerplate Code
Here’s what a typical implementation looks like with HttpClient
:
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
var httpClient = new HttpClient();
var apiKey = "your_api_key_here";
var city = "London";
// Construct the request URL
var url = $"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={apiKey}";
// Make the HTTP request
var response = await httpClient.GetAsync(url);
// Ensure the response is successful
response.EnsureSuccessStatusCode();
// Deserialize the JSON response
var jsonResponse = await response.Content.ReadAsStringAsync();
var weatherData = JsonSerializer.Deserialize<WeatherResponse>(jsonResponse);
// Display results
Console.WriteLine($"City: {weatherData.Name}");
Console.WriteLine($"Temperature: {weatherData.Main.Temp - 273.15:F2}°C");
Console.WriteLine($"Condition: {weatherData.Weather[0].Main} - {weatherData.Weather[0].Description}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
public class WeatherResponse
{
public string Name { get; set; }
public MainData Main { get; set; }
public WeatherDescription[] Weather { get; set; }
}
public class MainData
{
public double Temp { get; set; }
}
public class WeatherDescription
{
public string Main { get; set; }
public string Description { get; set; }
}
Problems with This Approach
Repetitive Boilerplate:
- Constructing URLs manually.
- Handling HTTP response validation and JSON deserialization repeatedly.
Hard to Maintain:
- Changes to API endpoints require modifying multiple areas in the codebase.
Type Safety:
- Query parameters and URL strings are prone to runtime errors.
Testing Complexity:
- Mocking
HttpClient
for unit tests can be cumbersome.
With Refit: Declarative API Consumption
Here’s the same example using Refit:
1. Define the API Contract
Create an interface for the API with Refit annotations:
using Refit;
using System.Threading.Tasks;
public interface IWeatherApi
{
[Get("/weather")]
Task<WeatherResponse> GetWeatherAsync([Query] string q, [Query] string appid);
}
2. Call the API
Use the Refit-generated client to interact with the API:
using Refit;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
// Create Refit client
var weatherApi = RestService.For<IWeatherApi>("https://api.openweathermap.org/data/2.5");
// Fetch weather data
var weatherData = await weatherApi.GetWeatherAsync("London", "your_api_key_here");
// Display results
Console.WriteLine($"City: {weatherData.Name}");
Console.WriteLine($"Temperature: {weatherData.Main.Temp - 273.15:F2}°C");
Console.WriteLine($"Condition: {weatherData.Weather[0].Main} - {weatherData.Weather[0].Description}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
public class WeatherResponse
{
public string Name { get; set; }
public MainData Main { get; set; }
public WeatherDescription[] Weather { get; set; }
}
public class MainData
{
public double Temp { get; set; }
}
public class WeatherDescription
{
public string Main { get; set; }
public string Description { get; set; }
}
How Refit Solves the Problems
Reduces Boilerplate:
- No need to manually construct URLs, handle query parameters, or manage JSON deserialization.
- Refit takes care of generating the
HttpClient
implementation.
Centralized Maintenance:
- API contracts are defined in a single interface. Changes to endpoints only require updates to the interface.
Type Safety:
- Strongly-typed query parameters (
[Query]
) and route segments ([Path]
) prevent runtime errors.
Simplifies Testing:
- Mocking the interface is straightforward for unit tests.
Side-by-Side Comparison
Is Refit Always the Best Choice?
When to Use Refit:
- When consuming multiple REST APIs.
- For projects requiring clean, maintainable code.
- When you need type-safe API interaction.
When to Stick to HttpClient:
- For highly customized requests (e.g., low-level HTTP headers).
- When working with non-standard APIs that don’t map well to Refit’s declarative style.
Conclusion
Refit doesn’t just simplify HTTP API consumption; it transforms how you write API-driven code in .NET. By abstracting the repetitive tasks of URL construction, query parameter handling, and deserialization, it allows you to focus on the logic of your application.
Whether you’re building a weather app, a social media client, or integrating with microservices, Refit can help you write cleaner, more maintainable code. Try it out, and you might never want to go back to the old HttpClient
way! 🚀
Happy coding! 😊