Skip to content
This repository has been archived by the owner on Sep 29, 2024. It is now read-only.

feat: Improve httpclient #246

Merged
merged 3 commits into from
Aug 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 54 additions & 32 deletions client/src/utils/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import axios, {
AxiosError,
AxiosResponse,
isAxiosError,
type AxiosRequestConfig,
} from 'axios';
Expand Down Expand Up @@ -44,10 +45,16 @@ export interface APIPostRequestParams<T extends APIPayload>
extends APIRequestParams {
payload?: T;
}
export interface APIPutRequestParams<T extends APIPayload>
extends APIPostRequestParams<T> {}
export interface APIDeleteRequestParams extends APIGetRequestParams {}

/** Exposed class */
export interface APIHttpClient {
get<T>(params: APIGetRequestParams): Promise<T>;
post<T, D extends APIPayload>(params: APIPostRequestParams<D>): Promise<T>;
put<T, D extends APIPayload>(params: APIPutRequestParams<D>): Promise<T>;
delete<T>(params: APIDeleteRequestParams): Promise<T>;
}

// Implementation
Expand Down Expand Up @@ -78,6 +85,28 @@ const resolveUrl = ({
? `${API_BASE_URL}${uri}${queryParams ? `?${queryParams}` : ''}`
: `${API_BASE_URL}/${API_VERSION}${uri}${queryParams ? `?${queryParams}` : ''}`;

const httpRequestWithCacheHandling = async <T>(
httpRequest: Promise<AxiosResponse<T, unknown>>,
): Promise<T> => {
const resp = await httpRequest.catch((err: AxiosError) => err);
const isErr = isAxiosError(resp);

if (!isErr && isSuccessResponse(resp.data)) {
return resp.data;
} else if (!isErr) {
throw resp;
}

// See if cached
const data = resp.response?.data as T;
if (!data) throw resp;

// Resolve if cached is OK
if (isSuccessResponse(data)) return data;

throw resp;
};

class HTTPClient implements APIHttpClient {
get = async <T>({
withCredentials,
Expand All @@ -89,23 +118,7 @@ class HTTPClient implements APIHttpClient {
? addCredentials(withCredentials, options ?? DEFAULT_OPTS)
: (options ?? DEFAULT_OPTS);

const resp = await axios.get<T>(url, opts).catch((err: AxiosError) => err);
const isErr = isAxiosError(resp);

if (!isErr && isSuccessResponse(resp.data)) {
return resp.data;
} else if (!isErr) {
throw resp;
}

// See if cached
const data = resp.response?.data as T;
if (!data) throw resp;

// Resolve if cached is OK
if (isSuccessResponse(data)) return data;

throw resp;
return httpRequestWithCacheHandling<T>(axios.get<T>(url, opts));
};

post = async <T, D extends APIPayload>({
Expand All @@ -119,25 +132,34 @@ class HTTPClient implements APIHttpClient {
? addCredentials(withCredentials, options ?? DEFAULT_OPTS)
: (options ?? DEFAULT_OPTS);

const resp = await axios
.post<T>(url, payload, opts)
.catch((err: AxiosError) => err);
const isErr = isAxiosError(resp);
return httpRequestWithCacheHandling<T>(axios.post<T>(url, payload, opts));
};

if (!isErr && isSuccessResponse(resp.data)) {
return resp.data;
} else if (!isErr) {
throw resp;
}
put = async <T, D extends APIPayload>({
payload,
withCredentials,
options,
...uri
}: APIPutRequestParams<D>): Promise<T> => {
const url = resolveUrl(uri);
const opts: AxiosRequestConfig = withCredentials
? addCredentials(withCredentials, options ?? DEFAULT_OPTS)
: (options ?? DEFAULT_OPTS);

// See if cached
const data = resp.response?.data as T;
if (!data) throw resp;
return httpRequestWithCacheHandling<T>(axios.put<T>(url, payload, opts));
};

// Resolve if cached is OK
if (isSuccessResponse(data)) return data;
delete = async <T>({
withCredentials,
options,
...uri
}: APIDeleteRequestParams): Promise<T> => {
const url = resolveUrl(uri);
const opts: AxiosRequestConfig = withCredentials
? addCredentials(withCredentials, options ?? DEFAULT_OPTS)
: (options ?? DEFAULT_OPTS);

throw resp;
return httpRequestWithCacheHandling<T>(axios.delete<T>(url, opts));
};
}

Expand Down