Last active
June 21, 2020 08:16
-
-
Save katoozi/a2b7679fc0adbab494b4abb554507bb1 to your computer and use it in GitHub Desktop.
react native api module with axios
This file contains hidden or 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 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'; |
This file contains hidden or 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
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; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
put all files in
src/api
folder.