Skip to content

Instantly share code, notes, and snippets.

@katoozi
Last active June 21, 2020 08:16
Show Gist options
  • Save katoozi/a2b7679fc0adbab494b4abb554507bb1 to your computer and use it in GitHub Desktop.
Save katoozi/a2b7679fc0adbab494b4abb554507bb1 to your computer and use it in GitHub Desktop.
react native api module with axios
import axios, { AxiosInstance, ResponseType, Method, AxiosResponse, AxiosRequestConfig } from 'axios';
import { storeData, getData } from '../helpers';
export const STORAGE_TOKEN_KEY_NAME = 'token';
const PROTOCOL = 'https://'; // protocols: 'http', 'https'
const BASE_URL = 'example.com/'; // include just domain or sub domain and domain name.
const LEVEL_ONE_ENDPOINT = 'api/v1/'; // first level uri. include versioning etc.
const TOKEN_PRIFIX = 'token '; // do not remove the space. samples: token, Bearer, etc.
// URL is the server address. it should used only with axios, because it does not have final endpoint.
const URL = `${PROTOCOL}${BASE_URL}${LEVEL_ONE_ENDPOINT}`;
// add your endpoints here.
export const END_POINTS = {
profile: 'accounts/profile/',
get_token: 'accounts/social-accounts/obtain-token/',
detail: ({ id }) => {
return `lessons/${id}/`;
},
};
// axios default config.
const DEFAULT_CONFIG: AxiosRequestConfig = {
baseURL: URL,
responseType: 'json' as ResponseType,
};
// will include this headers for each request.
const DEFAULT_HEADERS = {
'Content-type': 'application/json; charset=utf-8',
};
// ApiService stuff
// Symbols are completely unique identifiers.
// Just like its primitive counterparts, they can be created using the factory function Symbol() which returns a Symbol.
// The two variables below, singleton and singletonEnforcer are not the same, they are both unique.
// Imagine a really long random string is return by each Symbol() call.
//
// API_SERVICE_IDENTIFIER === SINGLETON_ENFORCER // false
//
// Simple usage:
// const sym = Symbol()
// const foo = {
// [sym]: 'someValue'
// }
// foo[sym] // 'someValue'
//
// WARNING: do not export this two variables
const API_SERVICE_IDENTIFIER = Symbol(); // a simple key that pointing to our ApiService.
const SINGLETON_ENFORCER = Symbol(); // a simple key we use it for prevent new initialization.
// ApiService is class that provide token saving and retrieving.
// ApiService design pattern in singleton
class ApiService {
// singleton stuff
constructor(enforcer: Symbol) {
if (enforcer !== SINGLETON_ENFORCER) {
throw new Error('Cannot construct singleton');
}
this.session = axios.create({ ...DEFAULT_CONFIG });
}
static get instance(): ApiService {
if (!this[API_SERVICE_IDENTIFIER]) {
this[API_SERVICE_IDENTIFIER] = new ApiService(SINGLETON_ENFORCER);
}
return this[API_SERVICE_IDENTIFIER];
}
// token related stuff
private _token = '';
set token(value: string) {
this._token = value;
storeData(STORAGE_TOKEN_KEY_NAME, value);
if (this.onChangeToken !== undefined) {
this.onChangeToken(value);
}
}
get token(): string {
return this._token;
}
onChangeToken: (value: string) => void;
/**
readTokenFromStorage will read token from key, value storage and save it.
@returns void
*/
readTokenFromStorage = () => {
getData(STORAGE_TOKEN_KEY_NAME).then(value => {
this._token = value === null ? '' : value;
if (this.onChangeToken !== undefined && this._token !== '') {
this.onChangeToken(this._token);
}
});
};
// axios and http request stuff
private session: AxiosInstance;
/**
requestWithToken will send a http request and include token Header Authorization key with token value.
@param {Method} method - http request method -> 'post', 'get', 'put', ...
@param {string} endpoint - api endpoint. endpoint will concatenate with URL
@param {object} data - http request body. this object will be convert to json.
@param {object} customHeaders - set custom http headers.
@param {AxiosRequestConfig} customConfig - set custom axios configuration.
@returns Promise<AxiosResponse>
*/
requestWithToken = <T>(
method: Method,
endpoint: string,
data: object = null,
params: object = null,
customHeaders: object = null,
customConfig: AxiosRequestConfig = null,
): Promise<AxiosResponse<T>> => {
const config = {
...DEFAULT_CONFIG,
headers: {
...DEFAULT_HEADERS,
...customHeaders,
Authorization: `${TOKEN_PRIFIX}${this._token}`,
},
...customConfig,
};
let endpoint_url = endpoint;
if (endpoint in END_POINTS) {
endpoint_url = END_POINTS[endpoint];
if (typeof endpoint_url === 'function') {
endpoint_url = endpoint_url({ ...params });
}
}
switch (method.toUpperCase()) {
case 'GET':
case 'OPTIONS':
case 'HEAD':
case 'DELETE':
return this.session[method.toLowerCase()](endpoint_url, config);
default:
return this.session[method.toLowerCase()](endpoint_url, data, config);
}
};
/**
request will send a http request.
@param {Method} method - http request method -> 'post', 'get', 'put', ...
@param {string} endpoint - api endpoint. endpoint will concatenate with URL
@param {object} data - http request body. this object will be convert to json.
@param {object} customHeaders - set custom http headers.
@param {AxiosRequestConfig} customConfig - set custom axios configuration.
@returns Promise<AxiosResponse>
*/
request = <T>(
method: Method,
endpoint: string,
data: object = null,
params: object = null,
customHeaders: object = null,
customConfig: AxiosRequestConfig = null,
): Promise<AxiosResponse<T>> => {
const config = {
...DEFAULT_CONFIG,
headers: { ...DEFAULT_HEADERS, ...customHeaders },
...customConfig,
};
let endpoint_url = endpoint;
if (endpoint in END_POINTS) {
endpoint_url = END_POINTS[endpoint];
if (typeof endpoint_url === 'function') {
endpoint_url = endpoint_url({ ...params });
}
}
switch (method.toUpperCase()) {
case 'GET':
case 'OPTIONS':
case 'HEAD':
case 'DELETE':
return this.session[method.toLowerCase()](endpoint_url, config);
default:
return this.session[method.toLowerCase()](endpoint_url, data, config);
}
};
}
export default ApiService.instance;
export * from './models';
interface ServerResponse {
status: number;
message: string;
content: object;
}
interface PaginatedServerResponse<T> extends ServerResponse {
content: {
count: number;
next: string;
previous: string;
results: T;
};
}
export interface LoginResponse extends ServerResponse {
content: {
token: string;
};
}
@katoozi
Copy link
Author

katoozi commented Jun 21, 2020

put all files in src/api folder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment