2 min read
Azure API Management: Policy Deep Dive
API Management policies transform requests and responses. Rate limiting, caching, authentication, transformation—all without changing backend code.
Policy Structure
<policies>
<inbound>
<!-- Applied before request goes to backend -->
</inbound>
<backend>
<!-- Applied when forwarding to backend -->
</backend>
<outbound>
<!-- Applied to response from backend -->
</outbound>
<on-error>
<!-- Applied when errors occur -->
</on-error>
</policies>
Authentication Policies
Validate JWT
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401">
<openid-config url="https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration" />
<audiences>
<audience>api://my-api</audience>
</audiences>
<issuers>
<issuer>https://sts.windows.net/{tenant}/</issuer>
</issuers>
<required-claims>
<claim name="roles" match="any">
<value>Admin</value>
<value>Reader</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
API Key
<inbound>
<check-header name="X-API-Key" failed-check-httpcode="401" failed-check-error-message="Invalid API key">
<value>secret-key-1</value>
<value>secret-key-2</value>
</check-header>
</inbound>
Rate Limiting
<inbound>
<!-- Rate limit by subscription key -->
<rate-limit calls="100" renewal-period="60" />
<!-- Rate limit by IP -->
<rate-limit-by-key calls="10" renewal-period="60"
counter-key="@(context.Request.IpAddress)" />
<!-- Quota (monthly limits) -->
<quota calls="10000" bandwidth="40000" renewal-period="2592000" />
</inbound>
Caching
<inbound>
<!-- Cache lookup -->
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false">
<vary-by-header>Accept</vary-by-header>
<vary-by-query-parameter>version</vary-by-query-parameter>
</cache-lookup>
</inbound>
<outbound>
<!-- Cache store -->
<cache-store duration="3600" />
</outbound>
Request Transformation
<inbound>
<!-- Set headers -->
<set-header name="X-Request-ID" exists-action="override">
<value>@(Guid.NewGuid().ToString())</value>
</set-header>
<!-- Set query parameters -->
<set-query-parameter name="api-version" exists-action="override">
<value>2020-11-01</value>
</set-query-parameter>
<!-- Rewrite URL -->
<rewrite-uri template="/api/v2/{path}" copy-unmatched-params="true" />
<!-- Set body -->
<set-body>@{
var body = context.Request.Body.As<JObject>();
body["timestamp"] = DateTime.UtcNow.ToString("o");
return body.ToString();
}</set-body>
</inbound>
Response Transformation
<outbound>
<!-- Remove headers -->
<set-header name="X-Powered-By" exists-action="delete" />
<!-- Transform JSON response -->
<set-body>@{
var response = context.Response.Body.As<JObject>();
return new JObject(
new JProperty("data", response),
new JProperty("meta", new JObject(
new JProperty("requestId", context.RequestId),
new JProperty("timestamp", DateTime.UtcNow)
))
).ToString();
}</set-body>
<!-- XML to JSON -->
<xml-to-json kind="javascript-friendly" apply="always" />
</outbound>
Conditional Logic
<inbound>
<choose>
<when condition="@(context.Request.Headers.GetValueOrDefault("X-Premium", "false") == "true")">
<rate-limit calls="1000" renewal-period="60" />
</when>
<otherwise>
<rate-limit calls="100" renewal-period="60" />
</otherwise>
</choose>
</inbound>
Error Handling
<on-error>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("error", new JObject(
new JProperty("code", context.Response.StatusCode),
new JProperty("message", context.LastError.Message),
new JProperty("requestId", context.RequestId)
))
).ToString();
}</set-body>
</on-error>
Send Request (Backend Composition)
<inbound>
<send-request mode="new" response-variable-name="userInfo" timeout="10">
<set-url>@($"https://users-api/users/{context.Request.MatchedParameters["userId"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<set-header name="X-User-Name">
<value>@(((IResponse)context.Variables["userInfo"]).Body.As<JObject>()["name"].ToString())</value>
</set-header>
</inbound>
Policies make API Management a powerful gateway.