#csharp #dotnet #backend

Day 26: Consuming External APIs

Welcome to Day 26. Your API rarely exists in a vacuum. You usually need to talk to other APIs (Stripe for payments, SendGrid for emails, or maybe just a public Weather API).

In Day 9 (Async), we simply used new HttpClient(). Never do this in production ASP.NET Core apps.

If you create a new HttpClient for every request, you will exhaust your server’s available sockets (“Socket Exhaustion”). Instead, ASP.NET Core provides IHttpClientFactory to manage the lifecycle of HTTP connections cleanly and automatically.

Setting Up IHttpClientFactory

First, register the service in your Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Register base HTTP abilities
builder.Services.AddHttpClient(); 

// EVEN BETTER: Register a named client with base configurations!
builder.Services.AddHttpClient("WeatherApi", client => 
{
    client.BaseAddress = new Uri("https://api.weatherapi.com/v1/");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
});

var app = builder.Build();

Using it in your Endpoints

Now, inject IHttpClientFactory into your controllers or Minimal APIs.

using System.Text.Json; // Native .NET JSON parser

app.MapGet("/weather/{city}", async (string city, IHttpClientFactory factory) => 
{
    // 1. Create a client using the factory, passing the name we registered
    var client = factory.CreateClient("WeatherApi");
    
    // 2. Make the request (Base URL is already set!)
    var response = await client.GetAsync($"current.json?key=MY_KEY&q={city}");
    
    if (!response.IsSuccessStatusCode) return Results.BadRequest("Failed to fetch weather");
    
    // 3. Read the stream and deserialize safely
    var content = await response.Content.ReadAsStringAsync();
    
    return Results.Ok(content);
});

Typed Clients (The Elegant Approach)

Instead of using magic strings like "WeatherApi", you can inject a strongly-typed class.

// The Typed Client
public class GitHubService 
{
    private readonly HttpClient _client;

    // The factory automatically pushes an HttpClient in here!
    public GitHubService(HttpClient client) 
    {
        _client = client;
        _client.BaseAddress = new Uri("https://api.github.com/");
        _client.DefaultRequestHeaders.Add("User-Agent", "MyDotNetApp");
    }

    public async Task<string> GetUserAsync(string username) 
    {
        return await _client.GetStringAsync($"users/{username}");
    }
}

Register it via builder.Services.AddHttpClient<GitHubService>(); and then simply inject GitHubService directly wherever you need it!

Challenge for Day 26

Register a typed HTTP Client pointing to the public https://pokeapi.co/api/v2/. Write a Minimal API /pokemon/{name} that uses your typed client to fetch data about that Pokemon and returns it to your browser.

Tomorrow: Caching (Speeding things up!).