← Back to all posts

Azure API Management in .NET

Azure API Management in .NET

Reading Time: 7 minutes

The Problem

As soon as an application grows beyond a single internal API, the edge of the system starts getting messy.

Typical problems look like this:

  • multiple APIs expose inconsistent authentication rules
  • rate limiting is implemented in some services but not others
  • external consumers need one stable public contract while backend services keep changing
  • teams want analytics, throttling, caching, and request transformation, but duplicating that logic in every API is expensive
  • internal services are forced to own gateway concerns that are not really part of their domain

A small system can often tolerate this for a while. A larger one cannot. If every API team has to solve authentication, quotas, versioning, request rewriting, and consumer onboarding on its own, the platform becomes inconsistent and hard to secure.

The Solution

Azure API Management (APIM) puts a managed gateway in front of your APIs and centralizes the cross-cutting concerns that should not be reimplemented in every backend.

At a practical level, APIM gives you:

  • a consistent API gateway for internal and external consumers
  • policies for authentication, rate limiting, caching, header manipulation, and request transformation
  • products and subscriptions for controlled API access
  • API versioning and revisions
  • analytics, tracing, and developer portal capabilities
  • a clean separation between client-facing contracts and backend implementation details

The backend APIs still focus on business logic. APIM focuses on the edge.

Where APIM Fits

The simplest mental model is:

  • backend APIs own business behavior
  • APIM owns the public gateway behavior

That means APIM is a good fit when you need to:

  • expose internal APIs safely to external consumers
  • apply consistent throttling or quotas
  • validate JWT tokens at the gateway
  • hide backend URLs and reshape incoming requests
  • roll out versioned APIs without breaking clients
  • publish APIs through a developer portal

It is not a magic replacement for good backend design. It is a gateway layer that keeps backend services from being polluted with gateway responsibilities.

Common APIM Concepts

A few terms matter immediately.

APIs

An API in APIM represents a published surface that points to one or more backend services.

Operations

Operations are the individual routes and methods inside that API, such as:

  • GET /orders/{id}
  • POST /orders

Products

A product is a package of APIs exposed to a consumer group. This is where you often decide who gets access to what.

Subscriptions

Subscriptions provide a key-based access model for consumers. They are useful when you want to identify callers, limit usage, or separate client access.

Policies

Policies are one of APIM’s biggest strengths. They let you declare gateway behavior such as:

  • validate a JWT
  • limit requests per minute
  • rewrite a URL
  • inject headers
  • cache responses
  • route to a different backend

A Typical Architecture

A common flow looks like this:

  1. A client calls the APIM gateway URL.
  2. APIM authenticates or identifies the caller.
  3. APIM applies policies such as rate limiting, header rewriting, or token validation.
  4. APIM forwards the request to the backend API.
  5. APIM returns the backend response, optionally transforming it.

This gives you one consistent front door for APIs, even when the backend is made up of multiple services.

Security and Permissions

APIM supports several patterns, but the most common are:

  • subscription keys for consumer identification and product access
  • OAuth 2.0 / OpenID Connect for delegated user or app authentication
  • JWT validation policies to enforce token checks at the gateway
  • managed identity for APIM itself when it calls Azure backends securely

A good default posture is:

  • do not hardcode subscription keys in code
  • store client secrets or keys in Azure Key Vault or secure configuration
  • use OAuth/JWT for real identity and authorization
  • use subscription keys as a consumer access and monitoring tool, not as your only security boundary

Example Policy Scenarios

Some of the highest-value APIM policies are simple ones.

Rate limiting

A gateway-level rate limit keeps noisy clients from overwhelming the backend.

JWT validation

Instead of every service implementing repetitive token validation middleware differently, APIM can validate tokens before the request ever reaches the API.

Header transformation

You can hide internal backend requirements from external consumers by translating or injecting headers in the gateway.

Response caching

For suitable read-heavy endpoints, APIM can reduce backend load by caching responses for a short period.

Calling an APIM-Backed API from .NET

From a .NET application’s perspective, APIM is just the API endpoint it talks to. The main difference is that the base URL points to the APIM gateway and the request may need a subscription key or bearer token.

Install the package if you want a strongly-typed configuration model with DI support:

dotnet add package Microsoft.Extensions.Http

Configuration

Store the APIM base URL and any required subscription key in configuration or Key Vault, not in code:

{
  "ApiGateway": {
    "BaseUrl": "https://your-api-management-name.azure-api.net/",
    "OrdersApiPath": "orders",
    "SubscriptionKey": "set-via-secret-store"
  }
}

Options class

public sealed class ApiGatewayOptions
{
    public const string SectionName = "ApiGateway";

    public string BaseUrl { get; set; } = string.Empty;
    public string OrdersApiPath { get; set; } = string.Empty;
    public string SubscriptionKey { get; set; } = string.Empty;
}

Register an HttpClient

using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddOptions<ApiGatewayOptions>()
    .Bind(builder.Configuration.GetSection(ApiGatewayOptions.SectionName));

builder.Services.AddHttpClient<OrdersApiClient>((provider, client) =>
{
    var options = provider
        .GetRequiredService<IOptions<ApiGatewayOptions>>()
        .Value;

    client.BaseAddress = new Uri(options.BaseUrl);
    client.Timeout = TimeSpan.FromSeconds(30);

    if (!string.IsNullOrWhiteSpace(options.SubscriptionKey))
    {
        client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", options.SubscriptionKey);
    }
});

Client implementation

using System.Net.Http.Json;
using Microsoft.Extensions.Options;

public sealed class OrdersApiClient
{
    private readonly HttpClient _httpClient;
    private readonly ApiGatewayOptions _options;
    private readonly ILogger<OrdersApiClient> _logger;

    public OrdersApiClient(
        HttpClient httpClient,
        IOptions<ApiGatewayOptions> options,
        ILogger<OrdersApiClient> logger)
    {
        _httpClient = httpClient;
        _options = options.Value;
        _logger = logger;
    }

    public async Task<OrderDto?> GetOrderAsync(int orderId, CancellationToken cancellationToken = default)
    {
        using var response = await _httpClient.GetAsync(
            $"{_options.OrdersApiPath}/{orderId}",
            cancellationToken);

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            return null;
        }

        response.EnsureSuccessStatusCode();

        var order = await response.Content.ReadFromJsonAsync<OrderDto>(cancellationToken: cancellationToken);
        _logger.LogInformation("Retrieved order {OrderId} via APIM", orderId);

        return order;
    }
}

public sealed class OrderDto
{
    public int Id { get; set; }
    public string Status { get; set; } = string.Empty;
    public decimal TotalAmount { get; set; }
}

This keeps the .NET side simple:

  • HttpClientFactory manages connection reuse correctly
  • the app knows only the APIM gateway URL, not the backend topology
  • the subscription key is injected from configuration rather than hardcoded

If your gateway uses OAuth instead of subscription keys, the same client pattern still works. You would attach a bearer token instead of the Ocp-Apim-Subscription-Key header.

Why This Separation Matters

Without APIM, a backend service often grows accidental gateway logic:

  • rate limits in middleware
  • custom API key validation
  • duplicated response caching
  • inconsistent versioning conventions
  • ad hoc analytics and access controls

With APIM, those concerns move to the gateway where they are easier to standardize and observe.

The result is cleaner backend code and a more predictable platform surface for consumers.

Practical Recommendations

A pragmatic default for Azure API Management looks like this:

  • use APIM as the public gateway for APIs that need consistent edge behavior
  • keep backend services focused on domain logic
  • prefer OAuth/JWT for authentication and authorization
  • use subscription keys for consumer tracking and access packaging where appropriate
  • centralize throttling, caching, and transformation policies in APIM
  • store APIM-related secrets in Key Vault or secured configuration
  • monitor usage, failures, and latency at the gateway as well as in the backend

Summary

Azure API Management is not just an API proxy. It is a platform boundary that standardizes how APIs are exposed, secured, versioned, and observed.

That matters because the biggest API problems in larger systems are rarely about routing alone. They are about consistency at the edge.

Used well, APIM gives you:

  • a stable client-facing gateway
  • centralized security and throttling
  • policy-based request and response control
  • cleaner backend services
  • better operational visibility

That is usually the difference between a collection of APIs and an API platform.

References