Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nuxt): support async transforms for data composables #26154

Merged
merged 4 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/3.api/2.composables/use-async-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ type AsyncDataOptions<DataT> = {
deep?: boolean
dedupe?: 'cancel' | 'defer'
default?: () => DataT | Ref<DataT> | null
transform?: (input: DataT) => DataT
transform?: (input: DataT) => DataT | Promise<DataT>
pick?: string[]
watch?: WatchSource[]
getCachedData?: (key: string) => DataT
Expand Down
6 changes: 3 additions & 3 deletions docs/3.api/2.composables/use-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ All fetch options can be given a `computed` or `ref` value. These will be watche
- `lazy`: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
- `immediate`: when set to `false`, will prevent the request from firing immediately. (defaults to `true`)
- `default`: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option
- `transform`: a function that can be used to alter `handler` function result after resolving
- `transform`: a function that can be used to alter `handler` function result after resolving | Promise<DataT>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L97 and L111 were likely modified unintentionally

danielroe marked this conversation as resolved.
Show resolved Hide resolved
- `getCachedData`: Provide a function which returns cached data. A _null_ or _undefined_ return value will trigger a fetch. By default, this is: `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]`, which only caches data when `payloadExtraction` is enabled.
- `pick`: only pick specified keys in this array from the `handler` function result
- `watch`: watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
Expand All @@ -108,7 +108,7 @@ If you provide a function or ref as the `url` parameter, or if you provide funct
::

::tip{icon="i-simple-icons-youtube" color="gray" to="https://www.youtube.com/watch?v=aQPR0xn-MMk" target="_blank"}
Learn how to use `transform` and `getCachedData` to avoid superfluous calls to an API and cache data for visitors on the client.
Learn how to use `transform` and `getCachedData` to avoid superfluous calls to an API and cache data for visitors | Promise<DataT>on the client.
danielroe marked this conversation as resolved.
Show resolved Hide resolved
::

## Return Values
Expand Down Expand Up @@ -148,7 +148,7 @@ type UseFetchOptions<DataT> = {
deep?: boolean
dedupe?: 'cancel' | 'defer'
default?: () => DataT
transform?: (input: DataT) => DataT
transform?: (input: DataT) => DataT | Promise<DataT>
pick?: string[]
watch?: WatchSource[] | false
}
Expand Down
6 changes: 3 additions & 3 deletions packages/nuxt/src/app/composables/asyncData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { asyncDataDefaults } from '#build/nuxt.config.mjs'

export type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'

export type _Transform<Input = any, Output = any> = (input: Input) => Output
export type _Transform<Input = any, Output = any> = (input: Input) => Output | Promise<Output>

export type PickFrom<T, K extends Array<string>> = T extends Array<any>
? T
Expand Down Expand Up @@ -283,13 +283,13 @@ export function useAsyncData<
reject(err)
}
})
.then((_result) => {
.then(async (_result) => {
// If this request is cancelled, resolve to the latest request.
if ((promise as any).cancelled) { return nuxtApp._asyncDataPromises[key] }

let result = _result as unknown as DataT
if (options.transform) {
result = options.transform(_result)
result = await options.transform(_result)
}
if (options.pick) {
result = pick(result as any, options.pick) as DataT
Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/basic-types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,16 @@ describe('composables', () => {
expectTypeOf(useLazyAsyncData<string>(() => $fetch('/test'), { default: () => 'test', transform: () => 'transformed' }).data).toEqualTypeOf<Ref<string>>()
})

it('supports asynchronous transform', () => {
const { data } = useAsyncData('test', () => $fetch('/test') as Promise<{ foo: 'bar' }>, {
async transform (data) {
await Promise.resolve()
return data.foo
}
})
expectTypeOf(data).toEqualTypeOf<Ref<'bar' | null>>()
})

it('infer request url string literal from server/api routes', () => {
// request can accept dynamic string type
const dynamicStringUrl = 'https://example.com/api'
Expand Down