Skip to content

Instantly share code, notes, and snippets.

@dersonsena
Created November 29, 2019 18:51
Show Gist options
  • Save dersonsena/9faa44e38246d0e401afe1408e619b13 to your computer and use it in GitHub Desktop.
Save dersonsena/9faa44e38246d0e401afe1408e619b13 to your computer and use it in GitHub Desktop.
Service Layer in React Project
// src/utils/services/Api.js
import Http from './Http'
/**
* @typedef {Api}
*/
export default class Api {
static base = '/v1';
/**
* @param {Http} http
*/
constructor (http) {
this._http = http;
}
/**
* @param {String} path
* @param {Object} options
* @return {this}
*/
static build (path = '', options = {}) {
const httpInstance = Http.build(Http.normalize(Api.base, path), options);
return new this(httpInstance);
}
get http () {
return this._http;
}
}
// src/utils/services/ApiBaseServices.js
import Api from './Api'
import queryString from 'query-string'
export default class ApiBaseServices extends Api {
/**
* @type {String}
*/
static resource = ''
/**
* @type {String}
*/
primaryKeyColumn = 'id'
/**
* @param {Object} http
* @param {String} resource
* @param {Object} options
*/
constructor (resource) {
const http = Api.build(resource).http
super(http)
}
/**
* @return {this}
*/
static build () {
return new this(this.resource)
}
/**
* @param {Object} parameters
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
search (parameters = {}) {
const params = {}
for (let fieldName in parameters) {
if (fieldName === 'filters') {
for (let filterName in parameters.filters) {
params[`filter[${filterName}]`] = parameters.filters[filterName];
}
continue;
}
params[fieldName] = parameters[fieldName];
}
const stringParams = '?' + queryString.stringify(params)
return this.http.get(`${stringParams}`)
.then(response => response)
}
/**
* @param {Object} record
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
create (record) {
return this.http.post('', record)
}
/**
* @param {String|Object} record
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
read (record) {
return this.http.get(`/${this.extractId(record)}`)
}
/**
* @param {Number} id
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
fetchById (id) {
return this.http.get(`/${id}`)
}
/**
* @param {Object} record
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
update (record) {
return this.http.put(`/${this.extractId(record)}`, record)
}
/**
* @param {Object} record
* @returns {*|PromiseLike<T | never>|Promise<T | never>}
*/
delete (record) {
return this.http.delete(`/${this.extractId(record)}`)
}
/**
* @param {String|Object} record
* @returns {String}
*/
extractId (record) {
if (typeof record === 'object') {
return record[this.primaryKeyColumn]
}
return String(record)
}
}
// src/domains/auth/AuthService.js
import Api from '~/utils/services/Api';
import { storeUserToken } from '~/domains/auth/helper';
/**
* @typedef AuthService
*/
export default class AuthService extends Api {
/**
* @param {String} email
* @param {String} password
* @returns {Promise}
*/
login (email, password) {
return this.http
.post('/auth/login', { email, password })
.then(response => {
storeUserToken(response.data.token);
return response;
});
}
}
// src/utils/services/Http.js
import HTTP from '~/_config/http';
/**
* @typedef {Http}
*/
export default class Http {
/**
* @param {String} path
* @param {Object} options
* @param {Object} http
*/
constructor (path, options, http = null) {
this.path = path;
this.http = http || HTTP;
}
/**
* @param {String} path
* @param {Object} options
*/
static build (path, options) {
return new this(path, options)
}
/**
* @param {Object} response
* @returns {Object}
*/
static then (response) {
let responseFallback = {};
if (!response) {
return responseFallback
}
responseFallback.headers = response.headers;
if (typeof response.data === 'string') {
responseFallback.data = JSON.parse(response.data);
}
return responseFallback;
}
/**
* @param {String} start
* @param {String} end
* @returns {String}
*/
static normalize (start, end) {
const slash = (end.indexOf('?', 0) !== -1 ? '' : '/')
return (`${start}${slash}${end}`.replace(/([^:]\/)\/+/g, '$1')).replace(/\/$/, '')
}
/**
* @param {String} url
* @returns {*|Promise<any>}
*/
get (url) {
return this.http
.get(this.constructor.normalize(this.path, url))
.then(this.constructor.then)
}
/**
* @param {String} url
* @param {Object} data
* @returns {*|Promise<any>}
*/
post (url, data) {
return this.http
.post(this.constructor.normalize(this.path, url), data)
.then(this.constructor.then)
}
/**
* @param {String} url
* @param {Object} data
* @returns {*|Promise<any>}
*/
put (url, data) {
return this.http
.put(this.constructor.normalize(this.path, url), data)
.then(this.constructor.then)
}
/**
* @param {String} url
* @param {Object} data
* @returns {*|Promise<any>}
*/
patch (url, data) {
return this.http
.patch(this.constructor.normalize(this.path, url), data)
.then(this.constructor.then)
}
/**
* @param {String} url
* @returns {*|Promise<any>}
*/
delete (url) {
return this.http
.delete(this.constructor.normalize(this.path, url))
.then(this.constructor.then)
}
}
// src/config/http.js
import axios from 'axios';
import { getUserToken, isAuthenticated } from '~/domains/auth/helper';
const HTTP = axios.create({
baseURL: process.env.REACT_APP_API_HOST,
timeout: 100000,
headers: {
'Content-Type': 'application/json'
},
transformResponse: [
function (data) {
return data
}
]
})
HTTP.interceptors.request.use(config => {
if (isAuthenticated()) {
config.headers['Authorization'] = `Bearer ${getUserToken()}`
}
return config;
},
error => Promise.reject(error));
export default HTTP;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment