Skip to content

Commit 6016846

Browse files
conico974Nicolas Dorseuil
andauthored
Add cache life inference from API response headers (#3467)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
1 parent cf1fae5 commit 6016846

File tree

1 file changed

+41
-3
lines changed
  • packages/gitbook/src/lib/data

1 file changed

+41
-3
lines changed

packages/gitbook/src/lib/data/api.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
type RenderIntegrationUI,
88
} from '@gitbook/api';
99
import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
10+
import { parse as parseCacheControl } from '@tusbar/cache-control';
1011
import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache';
1112
import { cache } from '../cache';
1213
import { DataFetcherError, wrapCacheDataFetcherError } from './errors';
@@ -146,6 +147,43 @@ export function createDataFetcher(
146147
};
147148
}
148149

150+
/**
151+
* Infer the cache life from the api response headers.
152+
* @param response The response from the API call.
153+
* @param defaultCacheLife The default cache life to use if not specified in the response.
154+
* @returns nothing
155+
*/
156+
function cacheLifeFromResponse(
157+
response: HttpResponse<unknown, unknown>,
158+
defaultCacheLife: 'days' | 'max' | 'hours' | 'minutes' | 'seconds'
159+
) {
160+
const cacheControlHeader = response.headers.get('x-gitbook-cache-control');
161+
const parsed = parseCacheControl(cacheControlHeader || '');
162+
const maxAge = parsed?.maxAge ?? parsed?.sharedMaxAge;
163+
if (maxAge) {
164+
return cacheLife({
165+
stale: 60 * 5, // This one is only for the client,
166+
revalidate: maxAge, // revalidate and expire are the same, we don't want stale data here
167+
expire: maxAge,
168+
});
169+
}
170+
// Typings in Next is "wrong" and does not allow us just use it as `cacheLife(defaultCacheLife)`
171+
switch (defaultCacheLife) {
172+
case 'days':
173+
return cacheLife('days');
174+
case 'max':
175+
return cacheLife('max');
176+
case 'hours':
177+
return cacheLife('hours');
178+
case 'minutes':
179+
return cacheLife('minutes');
180+
case 'seconds':
181+
return cacheLife('seconds');
182+
default:
183+
throw new Error(`Unknown default cache life: ${defaultCacheLife}`);
184+
}
185+
}
186+
149187
const getUserById = cache(async (input: DataFetcherInput, params: { userId: string }) => {
150188
'use cache';
151189
return wrapCacheDataFetcherError(async () => {
@@ -307,7 +345,7 @@ const getRevisionPageDocument = cache(
307345
);
308346

309347
cacheTag(...getCacheTagsFromResponse(res));
310-
cacheLife('max');
348+
cacheLifeFromResponse(res, 'max');
311349

312350
return res.data;
313351
}
@@ -361,7 +399,7 @@ const getDocument = cache(
361399
}
362400
);
363401
cacheTag(...getCacheTagsFromResponse(res));
364-
cacheLife('max');
402+
cacheLifeFromResponse(res, 'max');
365403
return res.data;
366404
});
367405
});
@@ -406,7 +444,7 @@ const getComputedDocument = cache(
406444
}
407445
);
408446
cacheTag(...getCacheTagsFromResponse(res));
409-
cacheLife('max');
447+
cacheLifeFromResponse(res, 'max');
410448
return res.data;
411449
}
412450
);

0 commit comments

Comments
 (0)