5
\$\begingroup\$

I have created a wrapper for Fetch to send requests to my API. The module exports api, and requests can be made with:

api.get({ url: '/users' })

or

api.post({ url: '/users', data: {id: 1, name: myName} })

After receiving the HTTP response from fetch I do some additional tasks, like update a token, then extract the JSON, camelCase the JSON keys, then return the JSON.

I'm mostly interested in a review of the api export, and handle errors in the fetch request.

const baseURL = 'http://mydomain.com/'
const csrfTokenExtractor = (response) => {
 const token = response.headers['x-csrf-token']
 if (token) {
 document.querySelector('meta[name=csrf-token]').setAttribute('content', token)
 }
 return response
}
const csrfHeader = () => {
 const token = document.querySelector('meta[name=csrf-token]').getAttribute('content')
 return {
 'X-CSRF-Token': token,
 }
}
const defaultHeader = () => {
 return {
 'X-App-Component': 'app',
 }
}
const sessionDetector = (response) => {
 if (response.status === 401) {
 window.location.replace(response.data.url)
 }
 return response
}
const buildURLQuery = (obj) =>
 Object.entries(obj)
 .map((pair) => pair.map(encodeURIComponent).join('='))
 .join('&')
const request = async ({ url, method, ...params }) => {
 params.credentials = 'same-origin'
 params.headers = Object.assign({}, params.headers || {}, defaultHeader())
 params.method = method
 if (method !== 'GET') {
 params.headers = Object.assign({}, params.headers || {}, { 'Content-Type': 'application/json' }, csrfHeader())
 }
 let response
 try {
 response = await fetch(`${baseURL}${url}`, { 
...params })
 if (!response.ok) {
 console.error(response)
 throw response
 }
 } catch (error) {
 sessionDetector(error)
 console.error(error)
 throw error
 }
 await csrfTokenExtractor(response)
 const json = await response.json()
 const formattedJson = camelcaseKeys(json, { deep: true })
 return formattedJson
}
const api = {
 get: ({ url, formData = {} }) => {
 const options = {
 method: 'GET',
 mode: 'cors',
 url: `${url}?${buildURLQuery(formData)}`,
 }
 return request(options)
 },
 post: ({ url, data = {} }) => {
 const options = {
 method: 'POST',
 url,
 body: JSON.stringify(data),
 }
 return request(options)
 },
 put: ({ url, data = {} }) => {
 const options = {
 method: 'PUT',
 url,
 body: JSON.stringify(data),
 }
 return request(options)
 },
 delete: ({ url }) => {
 const options = {
 method: 'DELETE',
 url,
 }
 return request(options)
 },
 request,
}
export default api
greybeard
7,3813 gold badges21 silver badges55 bronze badges
asked Jan 17, 2020 at 23:30
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

A few things stood out to me:

  1. You can extract out the implementation details behind where the token exists. Not a huge deal, but personally I would do this:

    const setAuthToken = (token) => document.querySelector('meta[name=csrf-token]').setAttribute('content', token)
    const getAuthToken = () =>document.querySelector('meta[name=csrf-token]').getAttribute('content')
    
  2. I don't think you should throw an error for a non-ok response. It's not a fatal error, so I don't think you should treat it as such.

    if (!response.ok) {
     console.error(response)
     // throw response
     return Promise.reject(response)
    }
    
  3. Be careful with the headers. The fetch api can also accept a Headers object and you can't merge it in with Object.assign. Headers is iterable, which is nice so you can use a for...of loop to manually extract the keys and set them into your object

    Read more about the Headers interface of the Fetch API here

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
answered Jan 18, 2020 at 0:42
\$\endgroup\$
2
  • \$\begingroup\$ Regarding #2: I'm no expert in Promises, but don't throw response and return Promise.reject(response) do exactly the same thing in this context? \$\endgroup\$ Commented Jul 16, 2020 at 8:05
  • \$\begingroup\$ @RoToRa In most situations, yes they are functionally the same. But the expressiveness and intent are different. While this is starting to creep into a philosophical discussion, should a successful api call with an undesirable response from a 3rd party be considered a fatal error on the callee's part? In all honesty, I wouldn't even consider throwing or rejecting in an ok === false condition. There should just be user feedback stating something went wrong. \$\endgroup\$ Commented Jul 16, 2020 at 19:10

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.