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!

Top comments (7)

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
 
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
 
jeansmaug profile image
jean-smaug

Nice !

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