Skip to content

Commit

Permalink
feat: add shouldInvalidateCache option to cache utils (#746)
Browse files Browse the repository at this point in the history
  • Loading branch information
yassilah authored Jan 16, 2023
1 parent b773ebe commit b91db6f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 29 deletions.
64 changes: 37 additions & 27 deletions docs/content/1.guide/1.introduction/5.cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: Nitro provides a powerful caching system built on top of the storag
## Usage

```js
const cachedFn = cachedEventHandler(fn, options)
const cachedFn = cachedEventHandler(fn, options);
```

### Options
Expand All @@ -18,65 +18,75 @@ const cachedFn = cachedEventHandler(fn, options)
- `maxAge`: Maximum age that cache is valid in seconds. Default is `1` second.
- `swr`: Enable Stale-While-Revalidate behavior. Enabled by default.
- `base`: Name of the storage mointpoint to use for caching (`/cache` by default)

- `shouldInvalidateCache`: A function that returns a boolean to invalidate the current cache and create a new one.

## Examples

**Example:** Cache an API handler

```js
// routes/cached.ts
const myFn = cachedEventHandler(async () => {
new Promise(resolve => setTimeout(resolve, 1000))
return `Response generated at ${new Date().toISOString()}`
}, { swr: true })
const myFn = cachedEventHandler(
async () => {
new Promise((resolve) => setTimeout(resolve, 1000));
return `Response generated at ${new Date().toISOString()}`;
},
{
swr: true,
}
);
```

**Example:** Cache a utility function

```js
// utils/index.ts
const myFn = cachedFunction(async () => {
new Promise(resolve => setTimeout(resolve, 1000))
return Math.random()
}, { swr: true })
const myFn = cachedFunction(
async () => {
new Promise((resolve) => setTimeout(resolve, 1000));
return Math.random();
},
{
swr: true,
}
);
```


**Example:** Enable Cache on a group of routes (**🧪 Experimental!**)

```js
// nitro.config.ts
import { defineNitroConfig } from 'nitropack'
import { defineNitroConfig } from "nitropack";

export default defineNitroConfig({
routeRules: {
'/blog/**': { swr: true }
}
})
"/blog/**": {
swr: true,
},
},
});
```


**Example:** Set cache storage mountpoint for a group of routes (**🧪 Experimental!**)

```js
// nitro.config.ts
import { defineNitroConfig } from 'nitropack'
import { defineNitroConfig } from "nitropack";

export default defineNitroConfig({
storage: {
'my-custom-storage': {
driver: 'redis',
url: 'redis://localhost:6379'
}
"my-custom-storage": {
driver: "redis",
url: "redis://localhost:6379",
},
},
routeRules: {
'/blog/**': {
"/blog/**": {
swr: true,
cache: {
base: '/my-custom-storage'
}
}
}
})
base: "/my-custom-storage",
},
},
},
});
```
9 changes: 7 additions & 2 deletions src/runtime/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface CacheOptions<T = any> {
getKey?: (...args: any[]) => string;
transform?: (entry: CacheEntry<T>, ...args: any[]) => any;
validate?: (entry: CacheEntry<T>) => boolean;
shouldInvalidateCache?: (...args: any[]) => boolean;
group?: string;
integrity?: any;
maxAge?: number;
Expand Down Expand Up @@ -53,7 +54,8 @@ export function defineCachedFunction<T = any>(

async function get(
key: string,
resolver: () => T | Promise<T>
resolver: () => T | Promise<T>,
shouldInvalidateCache?: boolean
): Promise<CacheEntry<T>> {
// Use extension for key to avoid conflicting with parent namespace (foo/bar and foo/bar/baz)
const cacheKey = [opts.base, group, name, key + ".json"]
Expand All @@ -69,6 +71,7 @@ export function defineCachedFunction<T = any>(
}

const expired =
shouldInvalidateCache ||
entry.integrity !== integrity ||
(ttl && Date.now() - (entry.mtime || 0) > ttl) ||
!validate(entry);
Expand Down Expand Up @@ -107,7 +110,8 @@ export function defineCachedFunction<T = any>(

return async (...args) => {
const key = (opts.getKey || getKey)(...args);
const entry = await get(key, () => fn(...args));
const shouldInvalidateCache = opts.shouldInvalidateCache?.(...args);
const entry = await get(key, () => fn(...args), shouldInvalidateCache);
let value = entry.value;
if (opts.transform) {
value = (await opts.transform(entry, ...args)) || value;
Expand All @@ -130,6 +134,7 @@ export interface ResponseCacheEntry<T = any> {

export interface CachedEventHandlerOptions<T = any>
extends Omit<CacheOptions<ResponseCacheEntry<T>>, "transform" | "validate"> {
shouldInvalidateCache?: (event: H3Event) => boolean;
getKey?: (event: H3Event) => string;
headersOnly?: boolean;
}
Expand Down

0 comments on commit b91db6f

Please sign in to comment.