Last active
April 19, 2022 04:10
-
-
Save knollfear/73bfa7ec3f3506c7ce7567aab8785225 to your computer and use it in GitHub Desktop.
API file for any JS project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _delay from "lodash/delay"; | |
import _get from "lodash/get"; | |
import _keys from "lodash/keys"; | |
import _filter from "lodash/filter"; | |
import _isNil from "lodash/isNil"; | |
import _isBoolean from "lodash/isBoolean"; | |
import _map from "lodash/map"; | |
import _isFunction from "lodash/isFunction" | |
import _isObject from "lodash/isObject" | |
const apiPath = process.env.VUE_APP_API_URL || `A URL`; | |
let config = { | |
apiPath, | |
fetchMode: "cors", | |
maxRetryCount: 2, | |
}; | |
// a promise friendly delay function | |
function delay(seconds) { | |
return new Promise((resolve) => { | |
_delay(resolve, seconds * 1000); | |
}); | |
} | |
function parseError(err) { | |
console.log(err); | |
const errMessage = err.errors | |
? err.errors[Object.keys(err.errors)[0]][0] | |
: err.message; | |
errMessage && alert(errMessage); | |
const message = errMessage; | |
try { | |
const code = Object.keys(err.errors)[0]; | |
const status = _get(err, "status"); | |
const requestId = _get(err, "requestId"); | |
const error = new Error(message); | |
error.code = code; | |
error.requestId = requestId; | |
error.root = err; | |
error.status = status; | |
return error; | |
} catch { | |
return new Error(message); | |
} | |
} | |
function configure(obj) { | |
config = { ...config, ...obj }; | |
} | |
function fetchJson(url, options = {}, retryCount = 0) { | |
// see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch | |
let isOk = false; | |
let httpStatus; | |
const headers = { | |
// Add any default headers here such as | |
// Accept: "application/json", | |
// "Content-Type": "application/json", | |
}; | |
const body = {}; | |
const merged = { | |
method: "GET", | |
mode: config.fetchMode, | |
redirect: "follow", | |
body, | |
...options, | |
headers: { ...headers, ...options.headers }, | |
}; | |
if (merged.method === "GET") delete merged.body; // otherwise fetch will throw an error | |
if (merged.params) { | |
// if query string parameters are specified then add them to the URL | |
// The merged.params here is just a plain JavaScript object with key and value | |
// For example {key1: value1, key2: value2} | |
// Get keys from the params object such as [key1, key2] etc | |
const paramKeys = _keys(merged.params); | |
// Filter out params with undefined or null values | |
const paramKeysToPass = _filter( | |
paramKeys, | |
(key) => !_isNil(_get(merged.params, key)) | |
); | |
const query = _map( | |
paramKeysToPass, | |
(key) => | |
`${encodeURIComponent(key)}=${encodeURIComponent( | |
_get(merged.params, key) | |
)}` | |
).join("&"); | |
url = query ? `${url}?${query}` : url; | |
} | |
console.log(url, merged) | |
return Promise.resolve() | |
.then(() => fetch(url, merged)) | |
.catch((err) => { | |
// this will capture network/timeout errors, because fetch does not consider http Status 5xx or 4xx as errors | |
if (retryCount < config.maxRetryCount) { | |
let backoff = retryCount * retryCount; | |
if (backoff < 1) backoff = 1; | |
return Promise.resolve() | |
.then(() => | |
console.log( | |
`Retrying count = ${retryCount}, Backoff = ${backoff}` | |
) | |
) | |
.then(() => delay(backoff)) | |
.then(() => fetchJson(url, options, retryCount + 1)); | |
} | |
throw parseError(err); | |
}) | |
.then((response) => { | |
console.log(response) | |
isOk = response.ok; | |
httpStatus = response.status; | |
return response; | |
}) | |
.then((response) => { | |
console.log(response) | |
if (_isFunction(response.text)) return response.text(); | |
return response; | |
}) | |
.then((text) => { | |
console.log(text) | |
let json; | |
try { | |
if (_isObject(text)) { | |
json = text; | |
} else { | |
json = JSON.parse(text); | |
} | |
} catch (err) { | |
if (httpStatus >= 400) { | |
if ( | |
httpStatus >= 501 && | |
retryCount < config.maxRetryCount | |
) { | |
let backoff = retryCount * retryCount; | |
if (backoff < 1) backoff = 1; | |
return Promise.resolve() | |
.then(() => | |
console.log( | |
`Retrying count = ${retryCount}, Backoff = ${backoff}` | |
) | |
) | |
.then(() => delay(backoff)) | |
.then(() => | |
fetchJson(url, options, retryCount + 1) | |
); | |
} | |
throw parseError({ | |
message: text, | |
status: httpStatus, | |
}); | |
} else { | |
throw parseError( | |
new Error("The server did not return a json response.") | |
); | |
} | |
} | |
return json; | |
}) | |
.then((json) => { | |
if (_isBoolean(isOk) && !isOk) { | |
throw parseError({ ...json, status: httpStatus }); | |
} else { | |
return json; | |
} | |
}); | |
} | |
// ---------- helper functions --------------- | |
// eslint-disable-next-line no-unused-vars | |
function httpApiMock(verb, urlPath, { data, params, response } = {}) { | |
console.log(`Performing an HTTP ${verb} to ${config.apiPath}/${urlPath}`); | |
data && console.log("DATA: ", data); | |
params && console.log("PARAMS: ", params); | |
response && console.log("RESPONSE: ", response); | |
return response; | |
} | |
function httpApiGet(urlPath, { params } = {}) { | |
console.log(urlPath) | |
return fetchJson(`${config.apiPath}/${urlPath}`, { | |
method: "GET", | |
//headers: authHeader(token), | |
params, | |
}); | |
} | |
// eslint-disable-next-line no-unused-vars | |
function httpApiPost(urlPath, { data = {}, params } = {}) { | |
console.log(data); | |
return fetchJson(`${config.apiPath}/${urlPath}`, { | |
method: "POST", | |
//headers: authHeader(token), | |
params, | |
body: JSON.stringify(data), | |
}); | |
} | |
// eslint-disable-next-line no-unused-vars | |
function httpApiPut(urlPath, { data, params } = {}) { | |
return fetchJson(`${config.apiPath}/${urlPath}`, { | |
method: "PUT", | |
//headers: authHeader(token), | |
params, | |
body: JSON.stringify(data), | |
}); | |
} | |
// eslint-disable-next-line no-unused-vars | |
function httpApiDelete(urlPath, { data, params } = {}) { | |
return fetchJson(`${config.apiPath}/${urlPath}`, { | |
method: "DELETE", | |
//headers: authHeader(token), | |
params, | |
body: JSON.stringify(data), | |
}); | |
} | |
// ---------- api calls --------------- | |
// API Functions Insertion Point (do not change this text, it is being used by hygen cli) | |
export { | |
configure, | |
config, | |
// API Export Insertion Point (do not change this text, it is being used by hygen cli) | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment