Skip to content

Commit

Permalink
feat: cache option for composables
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Dec 8, 2022
1 parent 8be0d1c commit 75f9438
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 32 deletions.
19 changes: 16 additions & 3 deletions README.md
Expand Up @@ -313,7 +313,9 @@ Customize your API's composable names with the `name` in your Nuxt config module
### `$party` (Respectively Camel-Cased API Name)

Returns the API response data.
Returns the raw response of the API endpoint.

Responses can be cached between function calls for the same path based on a calculated hash by setting `cache` to `true`.

**Type Declarations**

Expand All @@ -325,10 +327,14 @@ function $party<T = any>(

type ApiFetchOptions = Pick<
FetchOptions,
'onRequest' | 'onRequestError' | 'onResponse' | 'onResponseError' | 'headers' | 'method'
'onRequest' | 'onRequestError' | 'onResponse' | 'onResponseError' | 'query' | 'headers' | 'method'
> & {
query?: QueryObject
body?: Record<string, any>
/**
* Cache the response for the same request
* @default false
*/
cache?: boolean
}
```
Expand Down Expand Up @@ -377,6 +383,8 @@ Return values:

By default, Nuxt waits until a `refresh` is finished before it can be executed again. Passing `true` as parameter skips that wait.

Responses **are cached** between function calls for the same path based on a calculated hash. You can disable this behavior by setting `cache` to `false`.

**Type Declarations**

```ts
Expand Down Expand Up @@ -404,6 +412,11 @@ type UseApiDataOptions<T> = Pick<
| 'method'
> & {
body?: Record<string, any>
/**
* Cache the response for the same request
* @default true
*/
cache?: boolean
}
```
Expand Down
28 changes: 16 additions & 12 deletions src/runtime/composables/$api.ts
Expand Up @@ -9,6 +9,11 @@ export type ApiFetchOptions = Pick<
'onRequest' | 'onRequestError' | 'onResponse' | 'onResponseError' | 'query' | 'headers' | 'method'
> & {
body?: Record<string, any>
/**
* Cache the response for the same request
* @default false
*/
cache?: boolean
}

export type $Api = <T = any>(
Expand All @@ -22,17 +27,8 @@ export function _$api<T = any>(
opts: ApiFetchOptions = {},
): Promise<T> {
const nuxt = useNuxtApp()
const { query, headers, method, body, ...fetchOptions } = opts

const promiseMap: Map<string, Promise<T>> = nuxt._promiseMap = nuxt._promiseMap || new Map()
const key = `$party${hash([endpointId, path, query])}`

if (key in nuxt.payload.data)
return Promise.resolve(nuxt.payload.data[key])

if (promiseMap.has(key))
return promiseMap.get(key)!

const { query, headers, method, body, cache = false, ...fetchOptions } = opts
const endpointFetchOptions: EndpointFetchOptions = {
path,
query,
Expand All @@ -41,13 +37,21 @@ export function _$api<T = any>(
body,
}

const key = `$party${hash([endpointId, endpointFetchOptions])}`

if ((nuxt.isHydrating || cache) && key in nuxt.payload.data)
return Promise.resolve(nuxt.payload.data[key])

if (promiseMap.has(key))
return promiseMap.get(key)!

const request = $fetch(`/api/__api_party/${endpointId}`, {
...fetchOptions,
method: 'POST',
body: endpointFetchOptions,
}).then((response) => {
if (process.server)
nuxt.payload.data![key] = response
if (process.server || cache)
nuxt.payload.data[key] = response
promiseMap.delete(key)
return response
}) as Promise<T>
Expand Down
13 changes: 10 additions & 3 deletions src/runtime/composables/useApiData.ts
Expand Up @@ -34,6 +34,11 @@ export type UseApiDataOptions<T> = Pick<
| 'method'
> & {
body?: Record<string, any>
/**
* Cache the response for the same request
* @default true
*/
cache?: boolean
}

export type UseApiData = <T = any>(
Expand All @@ -57,6 +62,7 @@ export function _useApiData<T = any>(
headers,
method,
body,
cache = true,
...fetchOptions
} = opts

Expand Down Expand Up @@ -91,8 +97,8 @@ export function _useApiData<T = any>(

// Workaround to persist response client-side
// https://github.com/nuxt/framework/issues/8917
if (key.value in nuxt!.static.data)
return nuxt!.static.data[key.value]
if ((nuxt!.isHydrating || cache) && key.value in nuxt!.payload.data)
return nuxt!.payload.data[key.value]

controller = typeof AbortController !== 'undefined'
? new AbortController()
Expand All @@ -108,7 +114,8 @@ export function _useApiData<T = any>(
},
)) as T

nuxt!.static.data[key.value] = result
if (cache)
nuxt!.payload.data[key.value] = result

return result
},
Expand Down
15 changes: 1 addition & 14 deletions src/runtime/server.ts
Expand Up @@ -16,20 +16,7 @@ export default defineEventHandler(async (event): Promise<any> => {
})
}

const {
path,
query,
headers,
...fetchOptions
} = await readBody<EndpointFetchOptions>(event)

if (!path) {
throw createError({
statusCode: 404,
statusMessage: `Missing path for API endpoint "${endpointId}"`,
})
}

const { path = '', query, headers, ...fetchOptions } = await readBody<EndpointFetchOptions>(event)
const endpoint = endpoints[endpointId]
const _query = {
...endpoint.query,
Expand Down

0 comments on commit 75f9438

Please sign in to comment.