@icentris/onyx-sdks v1.0.20
onyx-services
The place where your API implementations go. Currently only VibeApi is available but will likely include service provider classes for additional vendors in the future (ie: content, localization, etc.)
Typically used for accessing external resources from within your getStore function -- onyx-app's <StoreWrapper /> provides a vibeApi instance to it's getStore call.
Conventions for Services
- Always return a
Promisefrom your service calls.- This allows for chaining the success / failure result from the calling locationthe calling location
- Avoid
async/awaitin favor ofPromise - Camelize returned data structure.
- Use
onyx-common/camelize, just pass the obj in it takes care of the rest:
- Use
import camelize from 'onyx-common/camelize'
const obj = {
first_name: 'Bob',
last_name: 'Johnson'
}
const camelizedObj = camelize(obj)
/*
camelizedObj = {
firstName: 'Bob',
lastName: 'Johnson'
}
*/- Always use traditional
function ()syntax for class methods- If arrow methods
() => {}are used, thethiscontext is lost from the compiled class.
- If arrow methods
fetchWrap helper
fetchWrap wraps the default fetch function to make it a bit more friendly to use. It accepts the following options:
- url: The URL to use for this request
- method:
POST,DELETEorGET.GETis default - data: an array of data to send with request
- urlMerge: if you provide placeholders in the
urlin the form ofhttp://google.com/{foo}/{bar}send in an array with{ 'foo': '1', 'bar': '2' }and it becomeshttp://google.com/1/2 - requestType:
jsonorformto change request headers. Has no impact onGETrequests - fetchOptions: direct fetch api options to pass through and override any convenience additions
It also wraps the return to make it easier to work with and ensure a promise is always returned.
Structure of a Service Class
To make services more manageable, they should be broken out into sub files for each area of responsibility (roughly mapped often to onyx-scenes-* packages, but not neccessarily). responsibilities and then rolled up into a single namespace.
Here's an example of how the VibeApi class is composed:
Base object with constructor and other shared functionality
VibeApi/VibeApiBase.js
const VibeApiBase = function () {}
VibeApiBase.prototype.constructor = function ({ baseUrl }) {
this.baseUrl = baseUrl
}
VibeApiBase.prototype.getUrl = function (path) {
return this.baseUrl + path
}
export default VibeApiBaseAdd one or more prototype decorators
VibeAPi/VibeApiAuth.js
import formurlencoded from 'onyx-common/form-urlencoded'
import urlencode from 'onyx-common/urlencode'
import fetchWrap from 'onyx-common/fetchWrap'
import camelize from 'onyx-common/camelize'
const VibeApiAuth = ({ prototype }) => {
prototype.login = function ({ username, password }) {
// ...rest of function
// example of how to use fetchWrap helper
return fetchWrap(payload)
.then(res => Promise.resolve(normalize(res)))
.catch(error => Promise.reject(Error('loginError', error)))
}
prototype.logout = function ({ authenticationToken }) {
// ...rest of function
// example of how to use fetchWrap helper
return fetchWrap(payload)
.then(() => Promise.resolve())
.catch(() => Promise.reject(Error('logoutError')))
}
}
export default VibeApiAuthCombine Base & decorator(s)
VibeApi/VibeApi.js
import VibeApi from './VibeApiBase'
import VibeApiAuth from './VibeApiAuth'
// add more decorators in the future
// import VibeApiResourceLibrary from './VibeApiResourceLibrary'
// decorate the prototype
VibeApiAuth(VibeApi)
// add more decorators in the future
// VibeApiResourceLibrary(VibeApi)
export default VibeApiThe final class is now usable like so:
import VibeApiFactory from 'onyx-services/VibeApi'
const VibeApi = new VibeApiFactory({
baseUrl: '/your/base/url'
})