Created
October 9, 2024 19:08
-
-
Save image72/e23ddcf695e634e19a5db2787935ab8c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type RequestConfig = RequestInit & { | |
baseURL?: string; | |
timeout?: number; | |
params?: Record<string, string>; | |
signal?: AbortSignal; | |
}; | |
type InterceptorFn = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>; | |
class FetchWrapper { | |
private baseURL: string; | |
private requestInterceptors: InterceptorFn[] = []; | |
private responseInterceptors: ((response: Response) => Response | Promise<Response>)[] = []; | |
constructor(baseURL: string = '') { | |
this.baseURL = baseURL; | |
} | |
private async applyInterceptors(config: RequestConfig): Promise<RequestConfig> { | |
for (const interceptor of this.requestInterceptors) { | |
config = await interceptor(config); | |
} | |
return config; | |
} | |
private async applyResponseInterceptors(response: Response): Promise<Response> { | |
for (const interceptor of this.responseInterceptors) { | |
response = await interceptor(response); | |
} | |
return response; | |
} | |
async request<T>(url: string, config: RequestConfig = {}): Promise<T> { | |
config = await this.applyInterceptors(config); | |
const signals: AbortSignal[] = []; | |
const timeout = config.timeout || 0; | |
if (timeout > 0) { | |
signals.push(AbortSignal.timeout(timeout)); | |
} | |
if (config.signal) { | |
signals.push(config.signal); | |
} | |
const signal = signals.length > 0 ? AbortSignal.any(signals) : undefined; | |
const fullURL = new URL(url, this.baseURL); | |
if (config.params) { | |
Object.entries(config.params).forEach(([key, value]) => { | |
fullURL.searchParams.append(key, value); | |
}); | |
} | |
try { | |
const response = await fetch(fullURL.toString(), { ...config, signal }); | |
const interceptedResponse = await this.applyResponseInterceptors(response); | |
if (!interceptedResponse.ok) { | |
throw new Error(`HTTP error! status: ${interceptedResponse.status}`); | |
} | |
return await interceptedResponse.json(); | |
} catch (error) { | |
if (error instanceof DOMException && error.name === 'AbortError') { | |
if (signal?.reason instanceof DOMException && signal.reason.name === 'TimeoutError') { | |
throw new Error('Request timed out'); | |
} else { | |
throw new Error('Request aborted'); | |
} | |
} | |
throw error; | |
} | |
} | |
get<T>(url: string, config: RequestConfig = {}): Promise<T> { | |
return this.request<T>(url, { ...config, method: 'GET' }); | |
} | |
post<T>(url: string, data?: any, config: RequestConfig = {}): Promise<T> { | |
return this.request<T>(url, { | |
...config, | |
method: 'POST', | |
body: JSON.stringify(data), | |
headers: { | |
'Content-Type': 'application/json', | |
...config.headers, | |
}, | |
}); | |
} | |
// Add other methods (put, delete, etc.) as needed | |
interceptors = { | |
request: { | |
use: (fn: InterceptorFn) => { | |
this.requestInterceptors.push(fn); | |
}, | |
}, | |
response: { | |
use: (fn: (response: Response) => Response | Promise<Response>) => { | |
this.responseInterceptors.push(fn); | |
}, | |
}, | |
}; | |
} | |
export default FetchWrapper; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment