interface FetchOptions extends RequestInit { url: string; params?: Record; body?: any; bodyType?: string; } export type Params = Record; export type FetchResponse = { data: T; rawResponse: Response; }; async function handleResponse(response: Response) { const text = await response.text(); if (response.ok) { return { data: JSON.parse(text), rawResponse: { ...response } }; } throw new Error(text || response.statusText); } function handleError(error: Error) { let jsonError; try { jsonError = JSON.parse(error.message); } catch (_) {} return Promise.reject(jsonError || error.message); } function _fetch(url: string, requestOptions: RequestInit): Promise> { return fetch(url, requestOptions).then(handleResponse).catch(handleError); } function getEndpointWithParams(endpoint: string, params?: Record) { let endpointWithParams = endpoint; if (params) { const urlSearchParams = new URLSearchParams(params); endpointWithParams += `?${urlSearchParams.toString()}`; } return endpointWithParams; } function convertToFormData(body: any) { const formData = new FormData(); for (const key in body) { formData.append(key, body[key]); } } function any( { url, params, body: _body, bodyType, ...options }: FetchOptions, method: string ): Promise> { let body = _body; if (bodyType === 'json') { body = JSON.stringify(body); } else if (bodyType === 'formdata') { body = convertToFormData(body); } const requestOptions = { method, body, ...options, }; return _fetch(getEndpointWithParams(url, params), requestOptions); } function get(options: FetchOptions): Promise> { return any(options, 'GET'); } function post(options: FetchOptions): Promise> { return any(options, 'POST'); } function patch(options: FetchOptions): Promise> { return any(options, 'PATCH'); } function put(options: FetchOptions): Promise> { return any(options, 'PUT'); } function del(options: FetchOptions): Promise> { return any(options, 'DELETE'); } const methods: Record(options: FetchOptions) => Promise>> = { get, post, put, patch, del, }; export default methods;