Enterwell / react-starter

Enterwell's template for web apps based on the React and Next.js.
https://react-starter.beta.enterwell.space
MIT License
17 stars 5 forks source link

[DX] Replace axios with fetch #376

Open AleksandarDev opened 1 year ago

AleksandarDev commented 1 year ago

We have abstraction layer with HttpService methods so we can replace existing implementation that is using Axios with fetch which will reduce bundle size and dependencies/number of update PRs.

radovix commented 1 year ago

Axios allows us to easily write all kind of interceptors (e.g. interceptor for checking whether request failed with 401 status code or interceptor for doing the token refreshing). How easy is to handle those cases without with just fetch?

AleksandarDev commented 1 year ago

@radovix As we have HttpService with a request function. That can easily be handled there by reading response.status and doing required logic. We don't encourage using axios in app directly anyway so adding interceptors globally is not that useful in our case.

Example:

  async function requestAsync(
    url: string,
    method: 'get' | 'post' | 'put' | 'delete',
    data?: any,
    headers?: Record<string, string>,
    skipAuth?: boolean
  ) {
    const token = skipAuth ? '' : await _getBearerTokenAsync()
    try {
        // Construct query URL (with params if we are doing GET)
        // We always featch with absolute URL, if provided with relative resolve to API url
        var urlResolved = new URL(isAbsoluteUrl(url) ? url : getApiUrl(url));
        if (method === 'get' && data) {
            urlResolved.search = new URLSearchParams(data).toString();
        }

        const response = await fetch(urlResolved, {
            method: method,
            body: method !== 'get' ? JSON.stringify(data) : undefined,
            headers: {
                Accept: 'application/json',
                Authorization: token,
                'Content-Type': 'application/json',
                ...headers
            },
        });

        // Return JSON - we expect response to always be JSON or empty
        if (response.status === 200) {
            try {
                return await response.json();
            } catch {
                return null;
            }
        }

        if (response.status === 403) {
            // TODO: Redirect to login
        }

        // Don't know how to handle other status codes
        let bodyText: string | null = null;
        try {
            bodyText = await response.text()
        } catch {
            bodyText = 'empty response';
        }
        throw new Error(`Got status ${response.statusText} (${response.status}): bodyText`);
    } catch(err) {
        console.error('Unknown API error', err);
        throw err;
    }
  }