Skip to content

Commit

Permalink
docs(cache): improve documentation and cachedFunction default optio…
Browse files Browse the repository at this point in the history
…ns (#1011)
  • Loading branch information
atinux authored Mar 3, 2023
1 parent df86bcf commit 3b3221b
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 51 deletions.
230 changes: 181 additions & 49 deletions docs/content/1.guide/1.introduction/5.cache.md
Original file line number Diff line number Diff line change
@@ -1,93 +1,225 @@
# Cache API

Nitro provides a powerful caching system built on top of the storage layer.
Nitro provides a powerful caching system built on top of the [storage layer](/guide/introduction/storage).

## Usage
It stores the data in the `cache` mountpoint.
- In development, it will use the [FS driver](https://unstorage.unjs.io/drivers/fs) writting to `.nitro/cache` or `.nuxt/cache` if using [Nuxt](https://nuxt.com).
- In production, it will use the [memory driver](https://unstorage.unjs.io/drivers/memory) by default.

To overwrite the production storage, set the `cache` mountpoint using the `storage` option:

::code-group
```ts [nitro.config.ts]
import { defineNitroConfig } from "nitropack";

```js
const cachedFn = cachedEventHandler(fn, options);
export default defineNitroConfig({
storage: {
cache: {
driver: 'redis',
/* redis connector options */
}
}
})
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
nitro: {
storage: {
cache: {
driver: 'redis',
/* redis connector options */
}
}
}
})
```
::

### Options
To overwrite the `cache` mountpoint in development, use the `devStorage` option to add the `cache` mountpoint.

- `name`: Handler name. It will be guessed from function name if not provided and fallback to `_` otherwise.
- `group`: Part of cache name. Useful to organize cache storage.
- `getKey`: A function that accepts same arguments of normal function and should generate cache key. If not provided, a built-in hash function will be used.
- `integrity`: A value that changing it, will invalidate all caches for function. By default will be computed from **function code**.
- `maxAge`: Maximum age that cache is valid in seconds. Default is `1` second.
- `staleMaxAge`: Maximum age that a stale cache is valid in seconds. If set to `-1` a stale value will still be sent to the client, while updating the cache in the background.
- `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.
- `shouldBypassCache`: A function that returns a boolean to bypass the current cache without invalidating the existing entry.
## Usage

::code-group
```ts [Router Handler]
// Cache an API handler
export default cachedEventHandler((event) => {
// My event handler
}, options);
```
```ts [Function]
const myFn = cachedFunction(() => {
// My function
}, options);
```
::

## Examples

**Example:** Cache an API handler
If you come from [Nuxt](https://nuxt.com), all the examples below should be placed inside the `server/` directory.

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

Cache a route with [stale-while-revalidate](https://www.rfc-editor.org/rfc/rfc5861#section-3) behavior for 10 second:

```ts [routes/cached.ts]
export default cachedEventHandler(async () => {
return `Response generated at ${new Date().toISOString()}`;
}, {
swr: true, maxAge: 10
});
```

**Example:** Cache a utility function
The response will be cached for 10 second and a stale value will be sent to the client while the cache is being updated in the background.

```js
// utils/index.ts
const myFn = cachedFunction(
async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return Math.random();
},
{
swr: true,
}
);
The cached answer will be store in development inside `.nitro/cache/handlers/_/*.json`.

### Function

Cache for 1 hour the result of a function fetching the GitHub stars for a repository:

::code-group
```ts [utils/github.ts]
export const cachedGHStars = cachedFunction(async (repo: string) => {
const data: any = await $fetch(`https://api.github.com/repos/${repo}`)

return data.stargazers_count
}, {
maxAge: 60 * 60,
name: 'ghStars',
getKey: (repo: string) => repo
})
```
```ts [api/stars/[...repo].ts]
export default eventHandler(async (event) => {
const repo = event.context.params.repo
const stars = await cachedGHStars(repo).catch(() => 0)

**Example:** Enable Cache on a group of routes (**🧪 Experimental!**)
return { repo, stars }
})
```
::

The stars will be cached in development inside `.nitro/cache/functions/ghStars/<owner>/<repo>.json` with `value` being the number of stars.

```json
{"expires":1677851092249,"value":43991,"mtime":1677847492540,"integrity":"ZUHcsxCWEH"}
```

```js
// nitro.config.ts

## Route Rules

This feature enable to add caching routes based on a glob pattern directly in the main configuration file.


::alert{type="primary"}
This features is still experimental and may evolve in the future.
::

Cache all the blog routes for 1 hour with `stale-while-revalidate` behavior:

::code-group
```ts [nitro.config.ts]
import { defineNitroConfig } from "nitropack";

export default defineNitroConfig({
routeRules: {
"/blog/**": {
swr: true,
swr: 60 * 60,
// or
cache: {
maxAge: 60 * 60
}
},
},
});
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
routeRules: {
"/blog/**": {
swr: 60 * 60,
// or
cache: {
maxAge: 60 * 60
}
},
}
});
```
::

**Example:** Set cache storage mountpoint for a group of routes (**🧪 Experimental!**)
If we want to use a custom storage mountpoint, we can use the `base` option. Let's store our cache result for the blog routes in a Redis storage for production:

```js
// nitro.config.ts
::code-group
```ts [nitro.config.ts]
import { defineNitroConfig } from "nitropack";

export default defineNitroConfig({
storage: {
"my-custom-storage": {
redis: {
driver: "redis",
url: "redis://localhost:6379",
},
},
routeRules: {
"/blog/**": {
swr: true,
swr: 60 * 60,
cache: {
base: "/my-custom-storage",
base: "redis",
},
},
},
});
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: "redis",
url: "redis://localhost:6379",
},
},
},
routeRules: {
"/blog/**": {
swr: 60 * 60,
cache: {
base: "redis",
},
},
},
});
```
::


## Options

The `cachedEventHandler` and `cachedFunction` functions accept the following options:
- `name`: Handler name.
- Type: `String`
- Default: guessed from function name if not provided and fallback to `_` otherwise.
- `group`: Part of cache name. Useful to organize cache storage.
- Type: `String`
- Default: `'nitro/handlers'` for handlers and `'nitro/functions'` for functions.
- `getKey`: A function that accepts the same arguments of the function and returns a cache key (`String`).
- Type: `Function`
- Default: If not provided, a built-in hash function will be used.
- `integrity`: A value that invalidates the cache when changed.
- Type: `String`
- Default: computed from **function code**, used in development to invalidate the cache when the function code changes.
- `maxAge`: Maximum age that cache is valid in seconds.
- Type: `Number`
- Default: `1` (second).
- `staleMaxAge`: Maximum age that a stale cache is valid in seconds. If set to `-1` a stale value will still be sent to the client, while updating the cache in the background.
- Type: `Number`
- Default: `0` (disabled).
- `swr`: Enable `stale-while-revalidate` behavior.
- Default: `true`
- `base`: Name of the storage mointpoint to use for caching.
- Default: `cache`.
- `shouldInvalidateCache`: A function that returns a `Boolean` to invalidate the current cache and create a new one.
- Type: `Function`
- `shouldBypassCache`: A function that returns a boolean to bypass the current cache without invalidating the existing entry.
- Type: `Function`
4 changes: 2 additions & 2 deletions src/runtime/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ const defaultCacheOptions = {

export function defineCachedFunction<T = any>(
fn: (...args) => T | Promise<T>,
opts: CacheOptions<T>
opts: CacheOptions<T> = {}
) {
opts = { ...defaultCacheOptions, ...opts };

const pending: { [key: string]: Promise<T> } = {};

// Normalize cache params
const group = opts.group || "nitro";
const group = opts.group || "nitro/functions";
const name = opts.name || fn.name || "_";
const integrity = hash([opts.integrity, fn, opts]);
const validate = opts.validate || (() => true);
Expand Down

0 comments on commit 3b3221b

Please sign in to comment.