Skip to content

Instantly share code, notes, and snippets.

@tranquan
Created August 5, 2021 20:06
Show Gist options
  • Save tranquan/5812d7e57d32ce28e28043766c4c0088 to your computer and use it in GitHub Desktop.
Save tranquan/5812d7e57d32ce28e28043766c4c0088 to your computer and use it in GitHub Desktop.
A simple API client for react, react-native
import { handleError } from "./errorHandler";
import { stringify } from "qs";
let BASE_URL = "http://localhost:8000/api/";
interface ClientConfig {
baseUrl?: string;
}
export function configureClient(configs: ClientConfig) {
if (configs.baseUrl) {
BASE_URL = configs.baseUrl;
}
}
export function getClientConfig() {
return {
baseUrl: BASE_URL,
};
}
interface StringMap {
[k: string]: string;
}
export interface GetOptions {
queryData: {};
headers: StringMap;
}
export interface BodyRequestOptions {
data: {};
isFormData: boolean;
headers: StringMap;
}
function serialize(obj: object): string {
return stringify(obj, { allowDots: true });
}
function initRequest(): RequestInit {
return {
mode: "cors",
cache: "default",
credentials: "same-origin",
headers: {
Accept: "application/json, */*",
"Content-Type": "application/json;charset=UTF-8",
},
};
}
/**
* Fetch: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
* Request: https://developer.mozilla.org/en-US/docs/Web/API/Request
*/
function request(method: string, path: string, options: Partial<GetOptions & BodyRequestOptions> = {}): Promise<any> {
const fetchRequest = initRequest();
fetchRequest.method = method;
if (options.isFormData) {
(fetchRequest.headers as any)["Content-Type"] = "application/form-data";
fetchRequest.body = options.data as any;
} else if (Object.keys(options.data ?? {}).length > 0) {
(fetchRequest.headers as any)["Content-Type"] = "application/json;charset=UTF-8";
fetchRequest.body = JSON.stringify(options.data);
}
const fetchUrl = BASE_URL + path;
return fetch(fetchUrl, fetchRequest)
.then(response => {
return handleError(response);
})
.then(response => {
return response.json();
})
.then(data => {
// fake slow loading
// return new Promise((res, rej) => {
// setTimeout(() => {
// res(data);
// }, 2000);
// });
// -- end
return data;
})
.catch(error => {
console.log(`error:`, error);
throw error;
});
}
export function getRequest(path: string, queryData: object = {}) {
return getRequestWithOptions(path, { queryData });
}
export function getRequestWithOptions(path: string, options: Partial<GetOptions> = {}) {
const { queryData } = options;
let pathWithQueryString = path;
if (queryData && Object.keys(queryData).length > 0) {
pathWithQueryString += "?" + serialize(queryData);
}
return request("GET", pathWithQueryString, options);
}
export function postRequest(path: string, data: object = {}, isFormData: boolean = false) {
return request("POST", path, { data, isFormData });
}
export function postRequestWithOptions(path: string, options: Partial<BodyRequestOptions> = {}) {
return request("POST", path, options);
}
export function deleteRequest(path: string, data: object = {}) {
return request("DELETE", path, { data });
}
import EventEmitter from "src/services/eventEmitter";
export class ApiError extends Error {
response: Response;
constructor(message: string, response: Response) {
super(message);
this.response = response.clone();
}
static isApiError(error: any) {
return error && error.response && error.response instanceof Response;
}
}
export enum ApiEvents {
/** http status = 400 */
BAD_REQUEST = "BAD_REQUEST",
/** http status = 401 */
UN_AUTHORIZED = "UN_AUTHORIZED",
/** http status = 403 */
FORBIDDEN = "FORBIDDEN",
/** http status = 302 */
RESPONSE_REDIRECT = "RESPONSE_REDIRECT",
}
export function isResponseError(response: Response) {
if (response.status < 200 || response.status > 299) {
return true;
}
return false;
}
function _handleError(response: Response) {
switch (response.status) {
case 302:
EventEmitter.notify(ApiEvents.RESPONSE_REDIRECT, { url: response.url });
break;
case 400:
EventEmitter.notify(ApiEvents.BAD_REQUEST, { url: response.url });
break;
case 401:
EventEmitter.notify(ApiEvents.UN_AUTHORIZED, { url: response.url });
break;
case 403:
EventEmitter.notify(ApiEvents.FORBIDDEN, { url: response.url });
break;
}
}
export function handleError(response: Response): Response {
if (isResponseError(response)) {
_handleError(response);
const message = `status: ${response.status}; url: ${response.url}`;
throw new ApiError(message, response);
}
// No error => continue
return response;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment