mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.2 KiB
123 lines
3.2 KiB
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios' |
|
import { Api } from 'nocodb-sdk' |
|
import type { Ref } from 'vue' |
|
import type { EventHook, MaybeRef } from '@vueuse/core' |
|
import { addAxiosInterceptors } from './interceptors' |
|
import { createEventHook, ref, unref, useGlobal } from '#imports' |
|
|
|
interface UseApiReturn<D = any, R = any> { |
|
api: Api<any> |
|
isLoading: Ref<boolean> |
|
error: Ref<AxiosError<D, R> | null> |
|
response: Ref<AxiosResponse<D, R> | null> |
|
onError: EventHook<AxiosError<D, R>>['on'] |
|
onResponse: EventHook<AxiosResponse<D, R>>['on'] |
|
} |
|
|
|
/** {@link Api} options */ |
|
interface CreateApiOptions { |
|
baseURL?: string |
|
} |
|
|
|
export function createApiInstance<SecurityDataType = any>(options: CreateApiOptions = {}): Api<SecurityDataType> { |
|
return addAxiosInterceptors( |
|
new Api<SecurityDataType>({ |
|
baseURL: options.baseURL ?? 'http://localhost:8080', |
|
}), |
|
) |
|
} |
|
|
|
interface UseApiProps<D = any> { |
|
/** additional axios config for requests */ |
|
axiosConfig?: MaybeRef<AxiosRequestConfig<D>> |
|
/** {@link Api} options */ |
|
apiOptions?: CreateApiOptions |
|
} |
|
|
|
/** |
|
* Api composable that provides loading, error and response refs, as well as event hooks for error and response. |
|
* |
|
* You can use this composable to generate a fresh api instance with its own loading and error refs. |
|
* |
|
* Any request called by useApi will be pushed into the global requests state and which toggles the global loading state. |
|
* |
|
* @example |
|
* ```js |
|
* const { api, isLoading, error, response, onError, onResponse } = useApi() |
|
* |
|
* const onSignIn = async () => { |
|
* const { token } = await api.auth.signIn(form) |
|
* } |
|
*/ |
|
export function useApi<Data = any, RequestConfig = any>(props: UseApiProps<Data> = {}): UseApiReturn<Data, RequestConfig> { |
|
const state = useGlobal() |
|
|
|
const isLoading = ref(false) |
|
|
|
const error = ref(null) |
|
|
|
const response = ref<any>(null) |
|
|
|
const errorHook = createEventHook<AxiosError<Data, RequestConfig>>() |
|
|
|
const responseHook = createEventHook<AxiosResponse<Data, RequestConfig>>() |
|
|
|
const api = createApiInstance(props.apiOptions) |
|
|
|
function addRequest() { |
|
state.runningRequests.value.push(state.runningRequests.value.length + 1) |
|
} |
|
|
|
function removeRequest() { |
|
state.runningRequests.value.pop() |
|
} |
|
|
|
api.instance.interceptors.request.use( |
|
(config) => { |
|
error.value = null |
|
response.value = null |
|
isLoading.value = true |
|
|
|
addRequest() |
|
|
|
return { |
|
...config, |
|
...unref(props), |
|
} |
|
}, |
|
(requestError) => { |
|
errorHook.trigger(requestError) |
|
error.value = requestError |
|
response.value = null |
|
isLoading.value = false |
|
|
|
removeRequest() |
|
|
|
return requestError |
|
}, |
|
) |
|
|
|
api.instance.interceptors.response.use( |
|
(apiResponse) => { |
|
responseHook.trigger(apiResponse as AxiosResponse<Data, RequestConfig>) |
|
// can't properly typecast |
|
response.value = apiResponse |
|
isLoading.value = false |
|
|
|
removeRequest() |
|
|
|
return apiResponse |
|
}, |
|
(apiError) => { |
|
errorHook.trigger(apiError) |
|
error.value = apiError |
|
isLoading.value = false |
|
|
|
removeRequest() |
|
|
|
return apiError |
|
}, |
|
) |
|
|
|
return { api, isLoading, response, error, onError: errorHook.on, onResponse: responseHook.on } |
|
}
|
|
|