Skip to content

Commit

Permalink
fix: manage custom expiry if available, exclude certain request methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
ItzNotABug committed May 24, 2024
1 parent 4f39498 commit f538b01
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
11 changes: 6 additions & 5 deletions middlewares/api-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ This module allows you to cache API responses for a specified period of time.
The responses are cached in memory after the first request, up until the function container is removed.\
You can check for `X-APPEXPRESS-API-CACHE` in the response header, the values will be any one of the below -

1. `HIT` means the response is cached
2. `MISS` means the response is not cached, probably this is the first request
3. `EXCLUDED` means the request is excluded for caching response via options or response header
1. `HIT` - response is cached
2. `MISS` - response is not cached, probably this is the first request
3. `EXCLUDED` - the request is excluded for caching response via options or response header
4. `IGNORED` - the request method is either `PUT`, `POST` or `DELETE` which are never cached

## Installation

Expand Down Expand Up @@ -66,8 +67,8 @@ express.get('/user/code', async (req, res) => {
```javascript
express.get('/search/results', async (req, res) => {
if (apiCache.hasCache(req.url)) {
res.empty();
return;
res.empty();
return;
}

const thirtySeconds = 30 * 1000;
Expand Down
31 changes: 28 additions & 3 deletions middlewares/api-cache/cache.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// noinspection JSUnresolvedReference
// noinspection JSUnresolvedReference, JSUnusedGlobalSymbols

import * as memoryCache from './memory.js';

Expand Down Expand Up @@ -69,7 +69,15 @@ export default {
* @param {Object} response - The `ResponseHandler` object.
*/
const serveCacheIfAvailable = (request, response) => {
if (isPathExcluded(request)) return;
if (isPathExcluded(request)) {
response.setHeaders({ CACHE_STATUS_HEADER_KEY: 'EXCLUDED' });
return;
}

if (!isRequestValid(request)) {
response.setHeaders({ CACHE_STATUS_HEADER_KEY: 'IGNORED' });
return;
}

const entry = memoryCache.get(request.url);
if (entry) {
Expand All @@ -78,7 +86,7 @@ const serveCacheIfAvailable = (request, response) => {
if (entry.isInfinite) {
headers['cache-control'] = 'public, immutable';
} else {
const maxAge = Math.round(configOptions.expirationTime / 1000);
const maxAge = Math.round(entry.timeout / 1000);
headers['cache-control'] = `max-age=${maxAge}`;
}
}
Expand Down Expand Up @@ -108,6 +116,11 @@ const saveCache = (request, interceptor) => {
return;
}

if (!isRequestValid(request)) {
headers[CACHE_STATUS_HEADER_KEY] = 'IGNORED';
return;
}

const excludeCache = headers['apicache-exclude'] ?? false;
if (excludeCache) {
headers[CACHE_STATUS_HEADER_KEY] = 'EXCLUDED';
Expand Down Expand Up @@ -137,3 +150,15 @@ const isPathExcluded = (request) => {
: exclude.test(request.path),
);
};

/**
* We shouldn't really cache the `PUT`, `POST` or `DELETE` method types.
*
* @param {Object} request - The `RequestHandler` object.
* @returns {boolean} False if the request method is one of the above-mentioned, true otherwise.
*/
const isRequestValid = (request) => {
const method = request.method;
const methodsToExclude = ['put', 'post', 'delete'];
return !methodsToExclude.includes(method);
};
2 changes: 2 additions & 0 deletions middlewares/api-cache/memory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @typedef {Object} CacheEntry
* @property {any} value - The cached data.
* @property {number} expiry - The expiry timeout without the timer.
* @property {number} timeout - Timeout identifier for the expiration.
* @property {boolean} isInfinite - Whether this is an immutable response.
*/
Expand All @@ -23,6 +24,7 @@ export const add = (timeout, request, interceptor) => {
const { body, headers, statusCode } = interceptor;

memoryCache[key] = {
expiry: timeout,
isInfinite: timeout === 0,
value: { body, headers, statusCode },
timeout: timeout === 0 ? null : setTimeout(() => remove(key), timeout),
Expand Down

0 comments on commit f538b01

Please sign in to comment.