5.1.5. Design API Integration and Caching
💡 First Principle: A gateway exists so that the rules every caller must obey — who you are, how often you may call, what shape your request must have — live in one place instead of being re-implemented in every backend. A cache exists because the cheapest request is the one that never reaches the origin. The two meet at the same design question: the closer a layer sits to the user, the more users share it — so the less personal the data it may hold.
Scenario: A manufacturer exposes order and inventory APIs to 300 partners. Some APIs run in Azure, others on servers inside two factories whose compliance rules say factory traffic must not leave the site. Partners complain about latency on the product catalog, and the database behind it is saturated by repeated identical reads.
Azure API Management in one picture. Every instance has a management plane (configuration) and a gateway (proxies requests, applies policies, collects telemetry); every tier except Consumption also includes a developer portal (API discovery and onboarding). The tier decides where the gateway can run and how it connects (Feature comparison of tiers):
| Requirement | Tier that satisfies it |
|---|---|
| Serverless, pay-per-call, automatic scaling | Consumption (no built-in cache, no developer portal) |
| Evaluation or development only | Developer (no SLA — never production) |
| Reach backends isolated in a virtual network, plus inbound private endpoint | Standard v2, Premium, Premium v2 (and Developer) |
| Inject the gateway itself into a virtual network | Premium, Premium v2 (and Developer) |
| One instance with gateways in several Azure regions | Premium (classic) only |
| Availability zones | Premium, Premium v2 |
| Gateway running on-premises or in another cloud | Self-hosted gateway — Developer or Premium |
The self-hosted gateway is a containerized copy of the gateway that runs on Docker or Kubernetes next to the APIs it fronts, so API traffic stays local while configuration and observability stay in the Azure instance. It needs outbound connectivity to Azure on port 443 and survives temporary disconnection using its last configuration (Self-hosted gateway overview). That is the factory answer in the scenario.
Policies an architect is expected to place (Policy reference):
| Need | Policy | Note |
|---|---|---|
| Reject calls without a valid token | validate-jwt or validate-azure-ad-token | Authentication stays at the gateway |
| Stop bursts from one caller | rate-limit (per subscription) or rate-limit-by-key (per any key, such as client IP) | rate-limit-by-key is not available in Consumption |
| Enforce a contracted volume over a period | quota or quota-by-key | Renewable or lifetime call and bandwidth limits |
| Allow or deny source addresses | ip-filter | Filters; does not throttle |
| Cache responses | cache-lookup / cache-store | Built-in cache per tier, or an external Redis-compatible cache |
| Spread load and protect a backend | set-backend-service to a backend pool | Load-balanced pools and circuit breaker rules |
Rate limit vs quota is a classic pairing: a rate limit protects the backend from spikes; a quota enforces the commercial agreement. A partner contract usually needs both.
Caching happens in layers, and each layer answers a different question:
| Layer | Caches | Shared by | Watch out for |
|---|---|---|---|
| Azure Front Door (edge) | Cacheable GET responses — images, CSS, JavaScript | Every user reaching that edge site | Never cache authenticated or personal responses |
| API Management response cache | Whole API responses | Every caller of that API | Consumption has no built-in cache — configure an external cache |
| Azure Managed Redis | Application data: query results, session state, shopping carts, leaderboards — held in memory, far faster than any disk-backed store, with a per-key expiry (TTL) that removes stale entries automatically | Every instance of the application | Consistency with the database is your code's job |
| In-process memory | Hot objects inside one instance | One instance only | Instances drift apart; expire aggressively |
Front Door caching rules worth knowing (Front Door caching): only GET requests are cacheable; cache duration comes from Cache-Control: s-maxage, then max-age, then Expires, and defaults to a random one to three days when the origin sends none; private, no-cache and no-store are honored even if a rules-engine override says otherwise. Microsoft recommends separate routes for static and dynamic content, with caching disabled on the dynamic one. Version asset URLs on each release so users get new files immediately. Front Door (classic) retires on March 31, 2027 — design on Standard or Premium.
Redis: use the current service name. Azure Managed Redis (built on Redis Enterprise) is the service to recommend for new designs. Azure Cache for Redis Basic, Standard and Premium retire on September 30, 2028, and the Enterprise and Enterprise Flash tiers on March 31, 2027 (Retirement FAQ). Azure Managed Redis tiers are chosen by memory-to-vCPU ratio (What is Azure Managed Redis?):
| Tier | Shape | Choose when |
|---|---|---|
| Memory Optimized | 8:1 memory to vCPU | Large data sets, modest throughput, dev/test |
| Balanced | 4:1 | Standard workloads |
| Compute Optimized | 2:1 | Maximum throughput |
| Flash Optimized | RAM plus NVMe | Very large, cost-sensitive caches — no active geo-replication and no search |
Active geo-replication, Redis JSON and search (including vector search) are available on the in-memory tiers. Instances are clustered by default, and high availability can be disabled only for dev/test because doing so risks data loss and downtime.
Cache-aside is the default application pattern (Cache-Aside pattern): read from the cache; on a miss, load from the data store, add it to the cache with an expiry, and return it. On a write, update the data store first, then invalidate the cache entry — reversing the order opens a window in which a reader reloads the old value into the cache. If read-after-write freshness is required, consider write-through caching instead.
⚠️ Exam Trap: "Enable caching on the whole Front Door endpoint and use Use Query String so each user gets their own copy." Query strings do not identify users, and caching an authenticated API at a shared edge is how one customer's data gets served to another. Split the routes and cache only the static one.
⚠️ Common Pitfall: Picking the Developer tier for a small production API because it has the features. It has no SLA; it exists for evaluation and development.
Key Trade-Offs:
- One gateway vs many: a single APIM instance gives one policy surface and one developer portal, but the tier you need for multi-region or self-hosted gateways (Premium) is the most expensive one.
- Freshness vs origin load: longer cache lifetimes cut origin load and latency and serve staler data; the right TTL is set by how much staleness the business can tolerate, not by what the cache allows.
Reflection Question: In the manufacturer scenario, which layer should cache the product catalog, which should cache per-partner order data (if any), and why does the factory requirement force the APIM tier before any other factor is considered?