Create custom APIM caching for POST operation

Introduction
APIM acts as a centralized API gateway, giving developers the ability to expose APIs securely whether to internal teams, partners, or external consumers. What I really like is how it brings everything together lifecycle management, security policies, rate limiting, and even analytics all under one roof.
Caching
Caching improves performance and efficiency by temporarily storing frequently requested data or API responses.
Benefits
Enhances Performance: Faster response times for repeated requests.
Reduces Backend Load: Minimizes traffic to backend services.
Improves Scalability: Helps handle high volumes of API calls smoothly.
Cost Optimization: Saves compute and network resources, especially in cloud environments.
Resilience: Continues to serve cached content during backend downtime.
Caching in APIM
Response Caching: Caches full HTTP responses.
Policy-based Caching: Use cache-lookup and cache-store policies to manage custom caching logic.
Prerequisites : Azure subscription and APIM instance (any plan)
Sample request payload mentioned below which will hit the D365FO API to get the given subparts details for given asset-id if not available in cache.
Request Payload
{ AssetID: AS001 , SubParts: {sb001, sb002, sb003) }
whenever new request comes it first check the unique key in cache-lookup if it exist return the response if doesn’t exist then forward the request to backend D365FO.
In my case unique key = assetid_subpart1_subpart2_subpart3 (AS001_sb001_sb002_sb003)
so to create key you have to extract the value from incoming request and set in variables
<set-variable name="assetid" value="@(context.Variables.GetValueOrDefault<JObject>("requestBody")?["AssetID"]?.ToString())" />
For Subparts youhave to iterate the array with inline C#
<set-variable name="subPartsJoined" value="@{var subPartsArray = (JArray)context.Variables.GetValueOrDefault<JObject>("requestBody")?["SubParts"];
return subPartsArray != null ? String.Join("_", subPartsArray.Select(t => t.ToString())) : "";}" />
Create another variable name it ‘cacheKey‘
<set-variable name="cacheKey" value="@($"{context.Variables["assetid"]}_{context.Variables["subPartsJoined"]}")" />
Now In API Inbound Policy add ‘Cache-Lookup‘ policy
<inbound>
<cache-lookup-value key="@((string)context.Variables["cacheKey"])" variable-name="cachedResponseValue" />
<choose>
<when condition="@(context.Variables.ContainsKey("cachedResponseValue"))">
<return-response>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@((string)context.Variables["cachedResponseValue"])</set-body>
</return-response>
</when>
<otherwise>
<set-backend-service base-url="http://api.****.com" />
<rewrite-uri template="/v1/current.json" />
<set-query-parameter name="key" exists-action="override">
<value>API Key value</value>
</set-query-parameter>
<set-query-parameter name="q" exists-action="override">
<value>@((string)context.Variables["assetid"])</value>
</set-query-parameter>
<set-header name="Accept-Encoding" exists-action="override">
<value>gzip, deflate</value>
</set-header>
</otherwise>
</choose>
</inbound>
Now, If Key doesn’t match it will forward the request to backend D365FO API and will store the response in cache-store.
In Outbound Policy add ‘Cache-store‘ policy and provide cache duration (time limit till when data will be available in cache.)
<outbound>
<base />
<set-variable name="responseValue" value="@((string)context.Response.Body.As<string>())" />
<cache-store-value key="@((string)context.Variables["cacheKey"])" value="@((string)context.Variables["responseValue"])" duration="20" />
<set-body template="none">@((string)context.Variables["responseValue"])</set-body>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
</outbound>
Save it, and hit your APIM API endpoint from postman or any other tool you use and notice the API response time.
You can also trace the request in APIM, you will get each step detail log in APIM.
1st request trace
2nd request trace with same payload
Conclusion
By default, caching is optimized for Get requests, but with the use of custom policies, it can also be configured to support Post operations.
Implementing caching whether for Get or Post endpoints can significantly enhance response times, decrease latency, and reduce infrastructure costs. For APIs experiencing high traffic volumes, leveraging APIM's caching capabilities is a practical and impactful optimization strategy.
Subscribe to my newsletter
Read articles from anurag prajesh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
