DEV Community

Alexander Kim
Alexander Kim

Posted on • Edited on

My Fetch Wrapper with async/await and TypeScript

Posting my wrapper for a native fetch() API:

const API_URL: string = process.env.YOUR_ENV_NAME || 'https://example.com';

export default async <T, B>(
  url: string,
  method = 'get',
  body: B | undefined = undefined,
  headers = {}
): Promise<T | { error: string }> => {
  const controller = new AbortController();
  try {
    const res = await fetch(`${API_URL}${url}`, {
      method: method.toUpperCase(),
      signal: controller.signal,
      body: typeof body === 'object' ? JSON.stringify(body) : undefined,
      mode: 'cors',
      headers: {
        'Content-type': 'application/json',
        ...headers
      }
    });
    if (!res.ok) {
      const error = await res.json();
      return { error: error.code };
    }
    return await res.json();
  } catch (err) {
    return { error: err };
  } finally {
    controller.abort();
  }
};

Enter fullscreen mode Exit fullscreen mode

And we can use it this way:

const result = await api<IResponse, IBody>('url', 'post', { name: 'asd' });
if (result.error) {
  // handle error;
} else {
  // handle successful response
}
Enter fullscreen mode Exit fullscreen mode

We can type our response as 1st type argument, and body as 2nd.

I wrote it to use in my React app. Improvements of this code snippet are welcome!

Oldest comments (7)

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt

POST is OK, but GET shouldn't have a response body. You should use querystrings.

Collapse
 
avxkim profile image
Alexander Kim

Yeah, that's why i set body to undefined by default, when calling this wrapper you also can omit a body.

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

You still have application/json header, which is affected by CORS.

Now, my goto is axios, which auto-detect body type

  • GET + { params: Object } => will convert URL to querystring. No header needed
  • POST + { data: Object } => application/json
  • POST + { data: Stringified query string } => application/x-www-form-urlencoded

Axios also allowes raise_for_status and error interceptors.

However, axios uses XMLHttpRequest. Not sure if this matters.

Thread Thread
 
avxkim profile image
Alexander Kim • Edited

Well, i've used axios with Vue a lot, it's more convenient, tbh. But it's an additional dependency, also they are not actively working on currently stalled issues in their repo. Axios has many neat features built-in. But i wonder about the future of this project.

Collapse
 
tomekbuszewski profile image
Tomasz Buszewski

Hey, very nice! A few remarks:

  • this function has a lot of arguments, perhaps try to use an object merged with default values;
  • method should be an enum, otherwise typos are welcome (path instead of patch for example);
  • it would be nice if you'd provide a structured response with status code – sometimes it makes a difference;
  • body shouldn't be allowed on get requests.

There are very minor things, no deal-breakers. Great job!

Collapse
 
avxkim profile image
Alexander Kim • Edited

Thank you for your tips, definitely need to improve this.

Collapse
 
jeansmaug profile image
jean-smaug

Nice !

For those who want a wrapper around fetch there is Ky ;)