Skip to content

Instantly share code, notes, and snippets.

@knollfear
Last active April 19, 2022 04:10
Show Gist options
  • Save knollfear/73bfa7ec3f3506c7ce7567aab8785225 to your computer and use it in GitHub Desktop.
Save knollfear/73bfa7ec3f3506c7ce7567aab8785225 to your computer and use it in GitHub Desktop.
API file for any JS project
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