@cmk/fe_utils
Version:
frontend utility library
179 lines (161 loc) • 4.95 kB
text/typescript
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
/** Definitions and Types */
/** ********************** */
/** HTTP Query method + 2 custom Response types with File (response=blob) */
export enum QUERY_METHOD {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
GET_FILE = 'GET_FILE', // GET with responseType: 'blob'
POST_GET_FILE = 'POST_GET_FILE', // POST with responseType: 'blob'
PATCH = 'PATCH',
}
/** initial timeouts per method */
enum QUERY_TIMEOUT {
POST = 1000 * 60 * 2,
POST_GET_FILE = 1000 * 60 * 5,
GET = 1000 * 60 * 2,
GET_FILE = 1000 * 60 * 5,
PUT = 1000 * 60 * 2,
DELETE = 1000 * 60 * 2,
PATCH = 1000 * 60 * 2,
}
/** Unknown Query Payload */
export type UnknownQueryPayloadType = Record<string, unknown>
/** AxiosConfig with some minor adjustments */
export type QueryConfigType = Omit<
AxiosRequestConfig,
'data' | 'url' | 'method'
> & {
disableRedirectToLogin?: boolean
}
export type QueryParamType<PayloadType> = QueryConfigType & {
url: string
payload?: PayloadType
}
/** The type of the query functions */
export type QueryFnType<
PayloadType = UnknownQueryPayloadType,
ResponseType = unknown,
> = (
params: QueryParamType<PayloadType>
) => Promise<AxiosResponse<ResponseType>>
export type QueryResponseType<ResponseType = unknown> = {
data: ResponseType | null
status: number | string
statusText: string
headers: Record<string, string>
error?: unknown
}
/** utilities for queries */
/** ******************* */
/** Get the content-type header for the payload based on payload (formData -> multipart/form-data)*/
const getContentHeader = (payload: unknown) => {
const contentType =
payload && payload instanceof FormData
? 'multipart/form-data'
: 'application/json'
const contentHeader = { 'Content-Type': contentType }
return contentHeader
}
/** utility to merge external and default headers */
const getHeaders = (
payload: unknown,
queryHeaders?: AxiosRequestConfig['headers']
) => {
return {
...getContentHeader(payload),
...(queryHeaders ?? {}),
}
}
/** utility to prepare the axios config object for the queries (all methods) */
const makeAxiosQueryConfig = <PayloadType>(
method: QUERY_METHOD,
params: QueryParamType<PayloadType>
) => {
// const
const { url, headers: queryHeaders, payload, ...queryConfig } = params
const headers: AxiosRequestConfig['headers'] = getHeaders(
payload,
queryHeaders
)
const responseTypeInjection =
method === QUERY_METHOD.GET_FILE || method === QUERY_METHOD.POST_GET_FILE
? { responseType: 'blob' as const }
: {}
const methodLower = method.toLowerCase()
const methodAdj = methodLower.includes('post')
? 'post'
: methodLower.includes('get')
? 'get'
: methodLower.includes('put')
? 'put'
: methodLower.includes('delete')
? 'delete'
: methodLower.includes('patch')
? 'patch'
: 'get'
const axiosQueryConfig: AxiosRequestConfig = {
timeout: QUERY_TIMEOUT[method],
...queryConfig,
data: payload,
method: methodAdj,
headers,
url,
...responseTypeInjection,
}
return axiosQueryConfig
}
/** the raw query for all methods */
const genericMethodQuery = async <
PayloadType = UnknownQueryPayloadType,
ResponseType = unknown,
>(
method: QUERY_METHOD,
params: QueryParamType<PayloadType>
): ReturnType<QueryFnType<PayloadType, ResponseType>> => {
const axiosQueryConfig = makeAxiosQueryConfig(method, params)
const response = axios(axiosQueryConfig)
return response
}
/** the query for all methods */
export const query = async <PayloadType = unknown, ResponseType = unknown>(
method: QUERY_METHOD,
params: QueryParamType<PayloadType>
): Promise<QueryResponseType<ResponseType>> => {
try {
const res = await genericMethodQuery<PayloadType, ResponseType>(
method,
params
)
const response = {
data: res.data,
status: res.status,
statusText: res.statusText,
headers: res?.headers as Record<string, string>,
}
return response
} catch (e) {
const error: AxiosError = e as AxiosError
const axiosResponse: AxiosError['response'] | null =
(axios.isAxiosError(e) ? error?.response : error?.response) ?? null
const altErrorCode =
(typeof e === 'object' && !!e && 'code' in e && (e?.code as string)) ||
null
const altMessage =
(typeof e === 'object' &&
!!e &&
'message' in e &&
(e?.message as string)) ||
null
const response = {
data: (e as AxiosError<ResponseType>)?.response?.data ?? null,
error: e,
status: axiosResponse?.status ?? altErrorCode ?? 'unknown',
statusText: axiosResponse?.statusText ?? altMessage ?? 'unknown',
headers: (axiosResponse?.headers as Record<string, string>) ?? {},
}
return response
}
}