Skip to content

Commit fc49a3c

Browse files
committed
add initial server-side caching section
1 parent 0d39b33 commit fc49a3c

File tree

1 file changed

+68
-2
lines changed

1 file changed

+68
-2
lines changed

docs/api-design-guidelines/caching.md

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,35 @@ Cache-Control: no-cache, no-store, must-revalidate, max-age=0
5656

5757
Whilst this may seem inefficient, there are actually a good reasons for doing so, Preventing any accidental client caching of sensitive/secure data, meaning it's much safer to default to a posture of no client caching by default.
5858

59+
## Server-Side Response Caching
60+
61+
APIs **SHOULD** implement server-controlled response caching that is independent of client-specified caching headers.
62+
63+
APIs **SHOULD** utilise their respective development ecosystem and take advantage of the available caching tools/libraries to support server-side response caching, for example if you are building your API with dotnet there is an [output caching](https://learn.microsoft.com/en-us/aspnet/core/performance/caching/output) middleware specifically for sever controlled caching, and for python there is a framework agnostic caching library called [cachews](https://github.com/Krukov/cashews).
64+
65+
When utilising an API gateway, APIs **SHOULD** make use of any response caching functionality, as this helps to reduces the load on the backend API; Azure Api Management (APIM) provides this functionality [through the use of policies](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-cache).
66+
67+
### Implementation Approaches
68+
69+
#### Basic Implementation Pattern
70+
71+
The general pattern for implementing server-side response caching is:
72+
73+
1. Check if request can be served from cache.
74+
2. If cached, return cached response.
75+
3. If not cached, generate response and store in cache.
76+
4. Return fresh response.
77+
78+
``` mermaid
79+
flowchart LR
80+
Request[Request Arrives] --> Lookup[Cache Key Lookup]
81+
Lookup --> Hit{Cache Hit?}
82+
Hit -->|Yes| Return[Return Cached Response]
83+
Hit -->|No| Generate[Generate Fresh Response]
84+
Generate --> CacheResponse[Cache Response]
85+
CacheResponse --> ReturnFresh[Return Fresh Response]
86+
```
87+
5988
## Client-Side Caching
6089

6190
While server-side caching is preferred, there are cases where client-side caching **MAY** be appropriate:
@@ -70,9 +99,9 @@ If your API *really* requires supporting `HTTP caching` , please observe the fol
7099

71100
**MUST** document all [cacheable](../api-design-guidelines/api-design.md#http-methods-semantics) `GET`, `HEAD`, and `POST` endpoints by declaring the support of [`Cache-Control`](https://datatracker.ietf.org/doc/html/rfc9111#section-5.2), [`Vary`](https://datatracker.ietf.org/doc/html/rfc9110#section-12.5.5), and [`ETag`](https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3) headers in response.
72101

73-
**MUST NOT** define the [`Expires`](https://datatracker.ietf.org/doc/html/rfc9111#section-5.3) header to prevent redundant and ambiguous definition of cache lifetime. A sensible default documentation of these headers is given below.
102+
**MUST NOT** define the [`Expires`](https://datatracker.ietf.org/doc/html/rfc9111#section-5.3) header to prevent redundant and ambiguous definition of cache lifetime.
74103

75-
**MUST** take care to specify the ability to support caching by defining the right caching boundaries, i.e. time-to-live and cache constraints, by providing sensible values for [`Cache-Control`](https://datatracker.ietf.org/doc/html/rfc9111#section-5.2) and [`Vary`](https://datatracker.ietf.org/doc/html/rfc9110#section-12.5.5) in your service. We will explain best practices below.
104+
**MUST** take care to specify the ability to support caching by defining the right caching boundaries, i.e. time-to-live and cache constraints, by providing sensible values for [`Cache-Control`](https://datatracker.ietf.org/doc/html/rfc9111#section-5.2) and [`Vary`](https://datatracker.ietf.org/doc/html/rfc9110#section-12.5.5) in your service.
76105

77106
APIs **SHOULD** use appropriate Cache-Control directives:
78107

@@ -512,3 +541,40 @@ APIs using caching **SHOULD** monitor:
512541
- **Cache Size**: Memory/storage consumption by the cache.
513542

514543
APIs **SHOULD** aim for a cache hit rate of at least 80% for cacheable resources.
544+
545+
### Response Headers for Monitoring
546+
547+
APIs **MAY** consider adding headers to help with debugging and monitoring:
548+
549+
``` text
550+
X-Cache: HIT
551+
X-Cache-TTL-Remaining: 286
552+
X-Cache-Key: products:fec65fb3-1e5e-4ff2-a6e0-a423f77f0000
553+
```
554+
555+
``` text
556+
X-Cache: HIT
557+
X-Cache-TTL-Remaining: 286
558+
X-Cache-Key: products:list:limit=10:offset=0:sort=name|asc
559+
```
560+
561+
### Example metrics capture
562+
563+
The below pseudo python example shows how you could manually log caching statistics, however there might libraries that could collect this telemetry for you with OpenTelemetry instrumentation such as [opentelemetry-instrumentation-fastapi](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/fastapi/fastapi.html).
564+
565+
``` python
566+
# Python example of cache monitoring
567+
def get_cached_response(cache_key):
568+
start_time = time.time()
569+
cached_response = cache.get(cache_key)
570+
lookup_time = time.time() - start_time
571+
572+
metrics.timing('cache.lookup_time', lookup_time)
573+
574+
if cached_response:
575+
metrics.increment('cache.hit')
576+
return cached_response
577+
else:
578+
metrics.increment('cache.miss')
579+
return None
580+
```

0 commit comments

Comments
 (0)