DEV Community

Cover image for What Happens Behind the Scenes When You Call an API in .NET
Libin Tom Baby
Libin Tom Baby

Posted on

What Happens Behind the Scenes When You Call an API in .NET

HttpClient lifecycle, DNS resolution, connection pooling, TLS handshake, request pipeline, response deserialization

You write await httpClient.GetAsync(url) and get a response back.

But what actually happens between those two moments?

This guide walks through every step — from DNS resolution to response deserialisation — explaining the mechanics that make HTTP calls work in .NET.


Step 1: HttpClient and IHttpClientFactory

HttpClient is the entry point for all outgoing HTTP calls in .NET.

The problem with new HttpClient()

// ❌ Never do this in a loop or per-request
using var client = new HttpClient();
Enter fullscreen mode Exit fullscreen mode

Creating a new HttpClient per request:

  • Does not reuse TCP connections — expensive
  • Exhausts socket connections under load (socket exhaustion)
  • Ignores DNS changes (stale DNS)

The solution: IHttpClientFactory

// Register once
builder.Services.AddHttpClient("OrdersApi", client =>
{
    client.BaseAddress = new Uri("https://api.example.com");
    client.Timeout = TimeSpan.FromSeconds(30);
});

// Inject and use
public class OrderService
{
    private readonly HttpClient _client;

    public OrderService(IHttpClientFactory factory)
    {
        _client = factory.CreateClient("OrdersApi");
    }
}
Enter fullscreen mode Exit fullscreen mode

IHttpClientFactory manages a pool of HttpMessageHandler instances, reusing connections safely while respecting DNS TTLs.


Step 2: DNS Resolution

Before a TCP connection can be established, the hostname must resolve to an IP address.

api.example.com104.21.45.22

  1. Check the local DNS cache
  2. If not cached, query the configured DNS server
  3. DNS server responds with an IP address (and TTL)
  4. Result is cached for the duration of the TTL

IHttpClientFactory rotates handlers on a schedule (default: 2 minutes) to pick up DNS changes and avoid stale IPs.


Step 3: TCP Connection

With the IP resolved, a TCP connection is established via the 3-way handshake.

Client → SYN      → Server
Client ← SYN-ACK  ← Server
Client → ACK       → Server
(Connection established)
Enter fullscreen mode Exit fullscreen mode

SocketsHttpHandler (the default in .NET 5+) maintains a connection pool per host. Once established, connections are reused for subsequent requests (keep-alive), which dramatically reduces latency under load.


Step 4: TLS Handshake (HTTPS)

For HTTPS, a TLS handshake occurs after TCP.

  1. Client sends ClientHello with supported cipher suites
  2. Server responds with certificate and chosen cipher
  3. Client verifies the certificate against trusted CAs
  4. Keys are exchanged; the session is encrypted

TLS session resumption and HTTP/2 multiplexing reduce this overhead on subsequent requests.


Step 5: HTTP Request

With the connection established and encrypted, the HTTP request is sent.

GET /orders HTTP/2
Host: api.example.com
Authorization: Bearer eyJhbGci...
Accept: application/json
Enter fullscreen mode Exit fullscreen mode

HTTP/1.1 vs HTTP/2

HTTP/1.1 sends requests sequentially per connection.
HTTP/2 multiplexes multiple requests over a single connection — significantly more efficient for APIs making many concurrent calls.

var handler = new SocketsHttpHandler
{
    EnableMultipleHttp2Connections = true
};
Enter fullscreen mode Exit fullscreen mode

Step 6: The Response

The server processes the request and returns a response.

HTTP/2 200 OK
Content-Type: application/json

{ "orders": [...] }
Enter fullscreen mode Exit fullscreen mode

Deserialisation in .NET

var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();

var orders = await response.Content.ReadFromJsonAsync<List<Order>>();
Enter fullscreen mode Exit fullscreen mode

ReadFromJsonAsync uses System.Text.Json — fast, low-allocation JSON deserialisation built into .NET.


Retry Policies with Polly

Real-world APIs fail. Transient failures (timeouts, 503s) should be retried.

builder.Services.AddHttpClient("OrdersApi")
    .AddTransientHttpErrorPolicy(policy =>
        policy.WaitAndRetryAsync(3, attempt =>
            TimeSpan.FromMilliseconds(200 * attempt)));
Enter fullscreen mode Exit fullscreen mode

Polly integrates directly with IHttpClientFactory — retry, circuit breaker, timeout, and fallback policies are all configurable.


Interview-Ready Summary

  • HttpClient should be created via IHttpClientFactory — never new HttpClient() per request
  • DNS resolution maps hostname to IP; IHttpClientFactory rotates handlers to avoid stale DNS
  • TCP 3-way handshake establishes the connection
  • TLS handshake encrypts the connection (HTTPS)
  • HTTP/2 multiplexes multiple requests over one connection
  • ReadFromJsonAsync deserialises the response using System.Text.Json
  • Use Polly for retry policies on transient failures

A strong interview answer:

"Calling an API in .NET goes through DNS resolution, a TCP handshake, a TLS handshake for HTTPS, and then the HTTP request over an established connection. IHttpClientFactory manages connection pooling and handler rotation to avoid socket exhaustion and stale DNS. On the response side, System.Text.Json handles deserialisation. Polly adds retry and circuit breaker policies for resilience."

Top comments (0)