integration-websocket-rest-api
Version:
A JavaScript library for easy integration of REST API and WebSocket communication with state management in JS applications.
157 lines (131 loc) • 4.46 kB
JavaScript
// installing the required dependencies for the integration of REST API
const axios = require("axios");
const moment = require("moment");
const numeral = require("numeral");
// Handle state changes using Redux
const {
store,
apiRequestSuccess,
apiRequestFailure,
} = require("./state-management-api");
class ApiClient {
// Handling the incoming configuration through the constructor
constructor(apiUrl, headers = {}) {
this.apiUrl = apiUrl;
this.headers = headers;
this.maxRetries = 3;
this.retryDelay = 1000;
this.timeout = 5000;
this.errorHandler = null;
this.requestInterceptors = [];
this.responseInterceptors = [];
// More configuration options can be added here in future versions
}
//setters for all the configurations
setGlobalHeaders(headers) {
this.headers = { ...this.headers, ...headers };
}
setMaxRetries(maxRetries) {
this.maxRetries = maxRetries;
}
setRetryDelay(retryDelay) {
this.retryDelay = retryDelay;
}
setTimeout(timeout) {
this.timeout = timeout;
}
setErrorHandler(errorHandler) {
this.errorHandler = errorHandler;
}
addRequestInterceptor(interceptor) {
this.requestInterceptors.push(interceptor);
}
addResponseInterceptor(interceptor) {
this.responseInterceptors.push(interceptor);
}
// Recursive function for deep mapping keys
static deepMapKeys(obj, mapFn) {
if (Array.isArray(obj)) {
return obj.map((item) => ApiClient.deepMapKeys(item, mapFn));
} else if (typeof obj === "object" && obj !== null) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [
mapFn(value, key),
ApiClient.deepMapKeys(value, mapFn),
])
);
} else {
return obj;
}
}
// Custom function to convert keys to camelCase
static toCamelCase(key) {
return key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
// Core function to send the axios request to the server
async sendRequest(method, data, retryCount = 0) {
try {
const config = {
method,
url: this.apiUrl,
headers: this.headers,
data,
timeout: this.timeout,
};
// Apply request interceptors if any
this.requestInterceptors.forEach((interceptor) => interceptor(config));
const response = await axios(config);
// Apply response interceptors if any
const modifiedResponse = this.responseInterceptors.reduce(
(resp, interceptor) => interceptor(resp),
response
);
const formattedData = ApiClient.deepMapKeys(
modifiedResponse.data,
(value, key) => {
// Format date strings using moment.js
if (moment(value, moment.ISO_8601, true).isValid()) {
return moment(value).format("YYYY-MM-DD");
}
// Format numbers using numeral.js
if (typeof value === "number") {
return numeral(value).format("0,0.00");
}
// Default: camelCase for other keys
return ApiClient.toCamelCase(key);
}
);
// Dispatch success action
store.dispatch(apiRequestSuccess(formattedData));
return formattedData;
} catch (error) {
if (retryCount < this.maxRetries) {
console.log(
`Retrying request. Attempt ${retryCount + 1} of ${this.maxRetries}`
);
await new Promise((resolve) => setTimeout(resolve, this.retryDelay));
return this.sendRequest(method, data, retryCount + 1);
}
// Custom error handling
if (this.errorHandler) {
this.errorHandler(error);
}
// Dispatch failure action
store.dispatch(apiRequestFailure(error));
// Handling errors with user-friendly messages and status codes
if (error.response) {
// The request was made, but the server responded with a status code outside the range of 2xx
throw new Error(
`API request failed with status ${error.response.status}: ${error.response.data}`
);
} else if (error.request) {
// The request was made but no response was received
throw new Error(`API request failed: No response received`);
} else {
// Something happened in setting up the request that triggered an Error
throw new Error(`API request setup failed: ${error.message}`);
}
}
}
}
module.exports = ApiClient;