Skip to content

Commit

Permalink
feat: support transform for async data fetcher (closes #12)
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Feb 21, 2023
1 parent 15d691c commit 84a0da1
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 29 deletions.
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,20 +391,26 @@ Responses **are cached** between function calls for the same path based on a cal
**Type Declarations**

```ts
function usePartyData<T = any>(
function usePartyData<
T = any,
Transform extends (res: T) => any = (res: T) => T,
>(
path: MaybeComputedRef<string>,
opts?: UseApiDataOptions<T>,
): AsyncData<T, FetchError | null | true>

type UseApiDataOptions<T> = Pick<
AsyncDataOptions<T>,
opts?: UseApiDataOptions<T, Transform>,
): AsyncData<T, FetchError>

type UseApiDataOptions<
T,
Transform extends _Transform<T, any> = _Transform<T, T>,
> = Pick<
AsyncDataOptions<T, Transform>,
| 'server'
| 'lazy'
| 'default'
| 'watch'
| 'immediate'
> & Pick<
ComputedOptions<FetchOptions>,
ComputedOptions<NitroFetchOptions<string>>,
| 'onRequest'
| 'onRequestError'
| 'onResponse'
Expand Down Expand Up @@ -453,10 +459,6 @@ const { data, pending, error, refresh } = await usePartyData('posts/1')
<script setup lang="ts">
const postId = ref(1)
const { data, pending, refresh, error } = await usePartyData('comments', {
// Custom query parameters to be added to the request, can be reactive
query: computed(() => ({
postId: postId.value
})),
// Whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
lazy: false,
// A factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option
Expand All @@ -465,8 +467,16 @@ const { data, pending, refresh, error } = await usePartyData('comments', {
}),
// Whether to fetch the data on the server (defaults to `true`)
server: true,
// A function that can be used to alter handler function result after resolving
transform: res => res,
// When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
immediate: true,
// Watch reactive sources to auto-refresh
watch: [],
// Custom query parameters to be added to the request, can be reactive
query: computed(() => ({
postId: postId.value
})),
// Custom headers to be sent with the request
headers: {
'X-Foo': 'bar'
Expand Down
4 changes: 3 additions & 1 deletion playground/pages/test/$testApi.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script setup lang="ts">
const data = await $testApi('todos')
import type { TestApiTodo } from '~/types'
const data = await $testApi<TestApiTodo>('todos')
useTestResult(data)
</script>
7 changes: 6 additions & 1 deletion playground/pages/test/useTestApiData.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<script setup lang="ts">
const { data } = await useTestApiData('todos')
import type { TestApiTodo } from '~/types'
const { data } = await useTestApiData<TestApiTodo[]>('todos',
{
transform: res => res.map(todo => ({ ...todo, isTransformed: true })),
})
useTestResult(data.value)
</script>
7 changes: 7 additions & 0 deletions playground/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ export interface JsonPlaceholderComment {
email: string
body: string
}

export interface TestApiTodo {
userId: number
id: number
title: string
completed: boolean
}
43 changes: 27 additions & 16 deletions src/runtime/composables/useApiData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import type { EndpointFetchOptions, MaybeComputedRef } from '../utils'
import { isFormData } from '../formData'
import { useAsyncData, useNuxtApp, useRuntimeConfig } from '#imports'

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

type ComputedOptions<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Function
? T[K]
Expand All @@ -19,11 +21,15 @@ type ComputedOptions<T extends Record<string, any>> = {
: Ref<T[K]> | T[K];
}

export type UseApiDataOptions<T> = Pick<
AsyncDataOptions<T>,
export type UseApiDataOptions<
T,
Transform extends _Transform<T, any> = _Transform<T, T>,
> = Pick<
AsyncDataOptions<T, Transform>,
| 'server'
| 'lazy'
| 'default'
| 'transform'
| 'watch'
| 'immediate'
> & Pick<
Expand Down Expand Up @@ -55,19 +61,23 @@ export type UseApiData = <T = any>(
opts?: UseApiDataOptions<T>,
) => AsyncData<T, FetchError | null | true>

export function _useApiData<T = any>(
export function _useApiData<
T = any,
Transform extends (res: T) => any = (res: T) => T,
>(
endpointId: string,
path: MaybeComputedRef<string>,
opts: UseApiDataOptions<T> = {},
opts: UseApiDataOptions<T, Transform> = {},
) {
const { apiParty } = useRuntimeConfig().public
const _path = computed(() => resolveUnref(path))
const {
server,
lazy,
default: defaultFn,
immediate,
transform,
watch,
immediate,
query,
headers,
method,
Expand All @@ -85,22 +95,23 @@ export function _useApiData<T = any>(

const _fetchOptions = reactive(fetchOptions)

const endpointFetchOptions: EndpointFetchOptions = reactive({
const _endpointFetchOptions: EndpointFetchOptions = reactive({
path: _path,
query,
headers: headersToObject(unref(headers)),
method,
})

const asyncDataOptions: AsyncDataOptions<T> = {
const _asyncDataOptions: AsyncDataOptions<T, Transform> = {
server,
lazy,
default: defaultFn,
immediate,
transform,
watch: [
endpointFetchOptions,
_endpointFetchOptions,
...(watch || []),
],
immediate,
}

let controller: AbortController
Expand All @@ -121,7 +132,7 @@ export function _useApiData<T = any>(
_$fetch = (event?.$fetch as typeof globalThis.$fetch) || globalThis.$fetch
}

return useAsyncData<T, FetchError>(
return useAsyncData<T, FetchError, Transform>(
key.value,
async (nuxt) => {
controller?.abort?.()
Expand All @@ -141,15 +152,15 @@ export function _useApiData<T = any>(
result = (await _$fetch<T>(_path.value, {
..._fetchOptions,
baseURL: endpoint.url,
method: endpointFetchOptions.method,
method: _endpointFetchOptions.method,
query: {
...endpoint.query,
...endpointFetchOptions.query,
..._endpointFetchOptions.query,
},
headers: {
...(endpoint.token && { Authorization: `Bearer ${endpoint.token}` }),
...endpoint.headers,
...endpointFetchOptions.headers,
..._endpointFetchOptions.headers,
},
body,
})) as T
Expand All @@ -162,7 +173,7 @@ export function _useApiData<T = any>(
signal: controller.signal,
method: 'POST',
body: {
...endpointFetchOptions,
..._endpointFetchOptions,
body: await serializeMaybeEncodedBody(body),
} satisfies EndpointFetchOptions,
},
Expand All @@ -174,6 +185,6 @@ export function _useApiData<T = any>(

return result
},
asyncDataOptions,
) as AsyncData<T, FetchError | null | true>
_asyncDataOptions,
) as AsyncData<T, FetchError>
}
3 changes: 3 additions & 0 deletions test/__snapshots__/e2e.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ exports[`nuxt-api-party > fetches data with useTestApiData 1`] = `
{
"completed": false,
"id": 1,
"isTransformed": true,
"title": "delectus aut autem",
"userId": 1,
},
{
"completed": false,
"id": 2,
"isTransformed": true,
"title": "quis ut nam facilis et officia qui",
"userId": 1,
},
{
"completed": false,
"id": 3,
"isTransformed": true,
"title": "fugiat veniam minus",
"userId": 1,
},
Expand Down

0 comments on commit 84a0da1

Please sign in to comment.