0
# HTTP Caching
1
2
Advanced HTTP caching system that follows RFC 7234 specifications with cache control, ETags, conditional requests, and intelligent cache validation.
3
4
## Capabilities
5
6
### Cache Configuration
7
8
Configure HTTP caching behavior through options that control cache storage, retrieval, and validation policies.
9
10
```javascript { .api }
11
/**
12
* Cache configuration options
13
*/
14
interface CacheOptions {
15
cachePath?: string; // Path to cache directory (required for caching)
16
cache?: CacheMode; // Cache mode controlling behavior
17
cacheAdditionalHeaders?: string[]; // Additional headers to store in cache
18
}
19
20
/**
21
* Cache modes following fetch specification
22
*/
23
type CacheMode =
24
| 'default' // Standard caching with revalidation
25
| 'no-store' // No caching
26
| 'reload' // Bypass cache, update with response
27
| 'no-cache' // Always revalidate
28
| 'force-cache' // Use cache regardless of staleness
29
| 'only-if-cached'; // Only use cached responses, error if none
30
```
31
32
**Usage Examples:**
33
34
```javascript
35
const fetch = require('make-fetch-happen');
36
37
// Enable caching with default behavior
38
const response = await fetch('https://api.example.com/data', {
39
cachePath: './my-cache'
40
});
41
42
// Different cache modes
43
const cachedFetch = fetch.defaults({ cachePath: './cache' });
44
45
// Use cache regardless of staleness
46
const staleResponse = await cachedFetch('https://api.example.com/config', {
47
cache: 'force-cache'
48
});
49
50
// Always revalidate before using cache
51
const freshResponse = await cachedFetch('https://api.example.com/users', {
52
cache: 'no-cache'
53
});
54
55
// Only use cache, fail if not cached
56
try {
57
const offlineResponse = await cachedFetch('https://api.example.com/offline-data', {
58
cache: 'only-if-cached'
59
});
60
} catch (error) {
61
console.log('Not cached:', error.code); // ENOTCACHED
62
}
63
```
64
65
### Cache Modes
66
67
Detailed behavior of each cache mode:
68
69
```javascript { .api }
70
/**
71
* Cache mode behaviors:
72
*
73
* 'default': Check cache for fresh response, use it. If stale, make conditional
74
* request for revalidation. If network fails, use stale cache.
75
*
76
* 'no-store': Never use cache, never store responses in cache.
77
*
78
* 'reload': Always make network request, store response in cache.
79
*
80
* 'no-cache': Always make conditional request if cached response exists,
81
* otherwise make normal request. Store response in cache.
82
*
83
* 'force-cache': Use any cached response regardless of staleness. If no
84
* cached response, make normal request and store in cache.
85
*
86
* 'only-if-cached': Use cached response regardless of staleness. If no
87
* cached response, throw ENOTCACHED error.
88
*/
89
```
90
91
### Additional Cache Headers
92
93
Store custom headers in the cache beyond the standard set:
94
95
```javascript { .api }
96
/**
97
* Default headers stored in cache:
98
* cache-control, content-encoding, content-language, content-type,
99
* date, etag, expires, last-modified, link, location, pragma, vary
100
*
101
* Add custom headers with cacheAdditionalHeaders option
102
*/
103
```
104
105
**Usage Examples:**
106
107
```javascript
108
// Store custom headers in cache
109
const response = await fetch('https://api.example.com/data', {
110
cachePath: './cache',
111
cacheAdditionalHeaders: [
112
'x-rate-limit-remaining',
113
'x-api-version',
114
'x-request-id'
115
]
116
});
117
118
// Custom headers will be available on cached responses
119
const cachedResponse = await fetch('https://api.example.com/data', {
120
cachePath: './cache',
121
cache: 'force-cache'
122
});
123
124
console.log(cachedResponse.headers.get('x-rate-limit-remaining'));
125
```
126
127
### Cache Response Headers
128
129
make-fetch-happen adds metadata headers to cached responses:
130
131
```javascript { .api }
132
/**
133
* Special headers added to cached responses:
134
*/
135
interface CacheResponseHeaders {
136
'x-local-cache': string; // Path to cache directory
137
'x-local-cache-key': string; // Unique cache key for this response
138
'x-local-cache-mode': 'stream'; // Always 'stream'
139
'x-local-cache-hash': string; // Integrity hash of cached content
140
'x-local-cache-status': CacheStatus; // How response was generated
141
'x-local-cache-time': string; // ISO timestamp of cache insertion
142
}
143
144
type CacheStatus =
145
| 'miss' // Not in cache, fetched from network
146
| 'hit' // Fresh cache hit
147
| 'stale' // Stale cache hit (returned without revalidation)
148
| 'revalidated' // Stale cache revalidated (304 response)
149
| 'updated' // Stale cache updated with new response
150
| 'skip'; // Cache bypassed
151
```
152
153
**Usage Examples:**
154
155
```javascript
156
const response = await fetch('https://api.example.com/data', {
157
cachePath: './cache'
158
});
159
160
// Check cache status
161
const cacheStatus = response.headers.get('x-local-cache-status');
162
console.log('Cache status:', cacheStatus); // 'miss', 'hit', etc.
163
164
// Get cache timing information
165
const cacheTime = response.headers.get('x-local-cache-time');
166
if (cacheTime) {
167
console.log('Cached at:', new Date(cacheTime));
168
}
169
```
170
171
### Manual Cache Access
172
173
Access cached entries directly using cacache:
174
175
```javascript { .api }
176
/**
177
* Manual cache access (requires cacache dependency)
178
*/
179
const cacache = require('cacache');
180
181
// Get cached entry using response headers
182
const getCachedEntry = async (response) => {
183
const cachePath = response.headers.get('x-local-cache');
184
const cacheKey = response.headers.get('x-local-cache-key');
185
186
if (cachePath && cacheKey) {
187
return await cacache.get(cachePath, cacheKey);
188
}
189
};
190
191
// Get content by integrity hash
192
const getCachedContent = async (response) => {
193
const cachePath = response.headers.get('x-local-cache');
194
const cacheHash = response.headers.get('x-local-cache-hash');
195
196
if (cachePath && cacheHash) {
197
return await cacache.get.byDigest(cachePath, cacheHash);
198
}
199
};
200
```
201
202
### Cache Invalidation
203
204
Caches are automatically invalidated for successful non-GET/HEAD requests:
205
206
```javascript { .api }
207
/**
208
* Automatic cache invalidation occurs when:
209
* - Request method is not GET or HEAD
210
* - Response status is 200-399
211
* - Request has a cachePath configured
212
*
213
* This ensures cached data remains consistent after mutations
214
*/
215
```
216
217
**Usage Examples:**
218
219
```javascript
220
const apiFetch = fetch.defaults({ cachePath: './cache' });
221
222
// GET request - uses and populates cache
223
const userData = await apiFetch('https://api.example.com/users/123');
224
225
// POST request - invalidates cache for this URL
226
await apiFetch('https://api.example.com/users/123', {
227
method: 'POST',
228
body: JSON.stringify({ name: 'Updated Name' }),
229
headers: { 'Content-Type': 'application/json' }
230
});
231
232
// Next GET will fetch fresh data
233
const updatedData = await apiFetch('https://api.example.com/users/123');
234
```
235
236
### Cache Storage Requirements
237
238
Caching behavior depends on proper cache directory setup:
239
240
```javascript { .api }
241
/**
242
* Cache requirements:
243
* - cachePath must be specified to enable caching
244
* - Directory must be writable by the process
245
* - Only GET and HEAD requests are cached
246
* - Responses must be consumed (json(), text(), buffer(), etc.) to be cached
247
*/
248
```
249
250
**Usage Examples:**
251
252
```javascript
253
// Ensure response is consumed for caching
254
const response = await fetch('https://api.example.com/data', {
255
cachePath: './cache'
256
});
257
258
// Must consume response body for caching to work
259
const data = await response.json(); // or .text(), .buffer(), etc.
260
261
// Stream responses are also cached
262
const streamResponse = await fetch('https://api.example.com/large-file', {
263
cachePath: './cache'
264
});
265
266
// Pipe to destination (response will be cached)
267
streamResponse.body.pipe(fs.createWriteStream('./downloaded-file'));
268
```