Skip to content

Instantly share code, notes, and snippets.

@afraser
Created July 8, 2021 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afraser/5015bc830e2d4f8b2e348d020c876db9 to your computer and use it in GitHub Desktop.
Save afraser/5015bc830e2d4f8b2e348d020c876db9 to your computer and use it in GitHub Desktop.
An axios-like wrapper around the fetch api that automatically aborts requests under a duplicate key. Provides an abort helper, and handles auth via aws-amplify.
import Auth from '@aws-amplify/auth'
/**
USAGE:
> api.post(`users/${id}`, { email: 'asdf@asdf.com' })
> api.get('users/${id}')
> api.patch(`users/${id}`, { email: 'adam@asdf.com' })
> api.delete(`users/${id}`)
*/
class ApiClient {
requests = {}
constructor () {
['get', 'post', 'put', 'patch', 'delete'].forEach(method => {
this[method] = (path, data, opts = {}) => {
const { headers, options, key, timeout } = opts
const controller = new AbortController()
const { signal } = controller
let timeoutId = null
if (key) {
this.abort(key)
this.requests[key] = controller
}
return Auth.currentSession().then(user =>
new Promise((resolve, reject) => {
if (timeout && this.requests.hasOwnProperty(key)) {
timeoutId = setTimeout(() => {
reject(new Error('Cancelled by client.'))
this.abort(key)
}, timeout)
}
fetch(
ApiClient.formatUrl(path),
{
headers: {
Authorization: `Bearer ${user.idToken.jwtToken}`,
'Accept-Encoding': 'deflate',
...headers
},
body: JSON.stringify(data),
method: method.toUpperCase(),
signal: signal,
...options
}
)
.then(resp => {
clearTimeout(timeoutId)
if (resp.status >= 400 || resp.error) reject(resp)
return (resp.status === 204) ? resp.text() : resp.json()
})
.then(
resp => {
if (resp.status >= 400 || resp.error) reject(resp)
else resolve(resp)
if (key) delete this.requests[key]
},
error => {
// aborted
if (error.code === 20) return
reject(error)
if (key) delete this.requests[key]
}
)
})
)
}
})
}
abort (key) {
if (key in this.requests) {
this.requests[key].abort()
delete this.requests[key]
}
}
static formatUrl (path) {
// if a full URL is supplied, don't mess with it...
if (path.startsWith('http')) return path
const basePath = GATEWAY
const adjustedPath = path[0] !== '/' ? `/${path}` : path
return `${basePath}${adjustedPath}`
}
}
export default new ApiClient()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment