API Submodule
API submodule allows you to communicate directly with HAPI Backend Services. This may come in handy when you want to map/transform your data to be VONQ compatible data or also may come in handy when you want to build custom components on your application instead of using our widgets.
Our Playground contains modules such as contract
. These modules have submodules such as the API submodule. The API submodule of each module contains functions to make HTTP requests to HAPI Backend such as getContracts
function.
API submodule is automatically authenticated and requires no setup.
Loading...
Canceling API Requests
Version 1.9 added the ability to cancel API requests.
When getContracts
function, for example, is called either by internal HAPI Elements logic, or by the ATS developer using window.hapi.contract.api.getContracts(ARGS)
, API submodule now adds (or updates existing) a JavaScript Map to the abortControllers
variable of window.hapi.contract.api.abortControllers
abortControllers
variable has Typescript type of:
abortControllers: Record<
string, //(name of the function)
Map<RawAxiosRequestConfig, AbortController>
>
There may be requests happening with different configuration passed to Axios, thus, the Map contains the config as the key so that you can selectively cancel requests based on the config parameters.
The developer could, for example, cancel all getContracts
requests as such:
// Change config of `getContracts` function to a mock API that returns 503 response in 59 seconds
// So that we have time to cancel, as part of the demo code
window.hapi.contract.api.configs.getContracts.url = 'https://mock.httpstatus.io/503?delay=59000'
// Considering that you (or HAPI Elements) have called this function
// which will take some time until BE returns a response (59 seconds in this example)
window.hapi.contract.api.getContracts()
// Later in your code, you decide to cancel all requests made with `getContracts` function
const abortControllersMapOfGetContractsFunction = window.hapi.contract.api.abortControllers.getContracts
if (abortControllersMapOfGetContractsFunction) {
const abortControllersOfGetContractsFunction = abortControllersMapOfGetContractsFunction.values()
abortControllersOfGetContractsFunction.forEach(abortController => {
abortController.abort()
})
// alternatively, you can loop over entries of the Map
// and selectively abort requests via the config of the request
const abortControllersKeyValuePairOfGetContractsFunction = abortControllersMapOfGetContractsFunction.entries()
abortControllersKeyValuePairOfGetContractsFunction.forEach(([config, abortController]) => {
if (config.url === 'https://mock.httpstatus.io/503?delay=59000') {
abortController.abort()
}
})
}
Using your own Backend proxy
HAPI Elements provides a way to use a backend proxy of your own. This may come in handy in cases such as:
- You already have a VONQ HAPI Backend integration
- You want to log everything in your database
The backend proxy, in its bare form, must match 1:1 of VONQ HAPI Backend service as which are documented on HAPI Backend Docs.
What we refer to as "1:1" are the following:
- URL structure
- Request headers
- Request arguments
- Request method
- Success and error responses returned being the same as HAPI Backend
Your backend proxy should forward the request to HAPI Backend service as is.
HAPI Elements uses JWT authentication thus, the X-Authorization: Bearer TOKENHERE
header will be passed automatically to all requests, and this should not be removed by accident on your backend proxy. If you need to disable the JWT Authorization headers in case you already have a backend integration with Basic authentication method HAPI Backend service offers, refer to the Disabling JWT Authorization header section.
So your backend proxy would look like this:
// Original HAPI Backend URL
https://marketplace.api.vonq.com/campaigns/{campaignId}
// Your proxy URL
https://your-url.com/integrations/vonq/campaigns/{campaignId}
So the URL after vonq.com/X/Y/Z
gets mapped to your-url.com/integrations/vonq/X/Y/Z
.
Please note that the examples below require HAPI Elements Script to be loaded. To know when script loads, you can refer to Listening to load event of HAPI Elements Script section.
When HAPI Elements script loads, these configurations must run first before any other code like state getters, setters, listeners
Changing the base URL of all HAPI Elements API Requests:
const yourAPIURL: string = "https://your-url.com/integrations/vonq"
window.hapiInjector.setConfig("apiHost", yourAPIURL)
// some other code
// inject after setting the config
window.hapiInjector.inject() //can be awaited to be notified when script has loaded
Legacy way for partners who are injecting Elements not via the provided injector.js script:
// Your code that injects HAPI Script via legacy methods
// that are not using the injector.js
// your inject code
// set API URL after HAPI Elements is already injected
const yourAPIURL: string = "https://your-url.com/integrations/vonq"
window.hapiApi.setBaseURL(yourAPIURL)
When using window.hapiApi.setBaseURL
(only recommended if you are not using injector.js
), you need to run window.hapiApi.setBaseURL
When the above function runs, all of the modules (like contract, product, campaign etc.) base URLs will get updated.
Changing the base timeout of HAPI Elements API Requests:
// default is 5000 milliseconds
const yourTimeoutInMilliseconds: number = 10000
window.hapiApi.setBaseTimeout(yourTimeoutInMilliseconds)
Setting additional base headers that will be on all of the requests
const yourHeaders: Record<string, string> = {
"custom-header-key": "custom-header-value"
}
window.hapiApi.setHeaders(yourHeaders)
Setting a base config that will be on all of the requests
const yourBaseConfig: Record<string, AxiosRequestConfig<any>> = {
"contract": {
"params": {
"some-param-that-will-be-on-all-requests": "some-value"
}
}
}
window.hapiApi.setBaseConfig(yourBaseConfig)
Disabling JWT Authorization header and removing the need to pass clientToken
If you already have a working backend integration with HAPI Backend with the Basic authentication method and don't want to change it to use the JWT authentication method, there is a way to disable the behaviour of adding by default the JWT Authorization headers.
To do this, you must add one more line of code before window.hapiInjector.inject()
function that loads the HAPI Elements.
// Before
const onLoadHapiElementsInjector = (clientToken: string) => {
window.hapiInjector.setConfig("clientToken", clientToken)
window.hapiInjector.inject()
}
// After
const onLoadHapiElementsInjector = (clientToken: string) => {
// window.hapiInjector.setConfig("clientToken", clientToken) //this line not needed anymore
window.hapiInjector.setConfig("useJWTAuthHeaders", false) // this line added
window.hapiInjector.inject()
}
Then X-Authorization: Bearer TOKENHERE
will not be passed to your backend proxy, so you can use your basic authentication method.
Preventing frontend from having references to clientToken
:
When you disable the JWT Authorization header, your backend proxy needs to handle authentication with either JWT or the old Basic Authentication method (less secure). However, since that code is on your backend, we can assume it will be much safer than the frontend. Using this will allow you so that the frontend code has no references to the clientToken
thus there will not be a way to grab the token from the frontend (unless you save your own user's token in localStorage or cookies, which is still a security risk on your end).
Changing the "X-Authorization" header key
In case you already have an "X-Authorization" header of your own being passed, then it would be conflicting with HAPI Elements' "X-Authorization"
You can change the default key of this header as such:
const customElementsJWTHeaderKey: string = "X-Something"
window.hapiApi.setJWTHeaderKey(customElementsJWTHeaderKey)
Customizing the API requests of HAPI Elements and changing your backend proxy structure
In case you have some requirements that make it not possible to have this "1:1" structure of the HAPI Backend API, or you already have an integration that does not match "1:1" and don't want to change it or you want to pass some additional headers to your backend you could change the configuration of HAPI Elements Javascript API, API submodule.
Each module in the API namespace, also references the baseURL automatically, but you might have a microservice architecture where for example, for "contract" you have a totally different URL. You can change the baseURL of each module (like contract, product, campaign etc.) to a different one that is separate from the main baseURL as such:
const mainBaseURL: string = "https://your-main-url.com/integrations/vonq"
window.hapiApi.setBaseURL(mainBaseURL)
const contractsBaseURL: string = "https://totally-different-url.com/integrations/vonq"
window.hapiApi.contract.setBaseURL(contractsBaseURL)
You can type in your dev tools console window.hapiApi
for a list of available modules under the API namespace.
Customizing per module the URL, request method, headers, parameters and body, timeout etc.
Each API module has some configuration available. We use axios package under the hood; thus, all available axios configs are also available for you to change.
const yourBaseConfigForContracts: AxiosRequestConfig<any> = {
"params": {
"some-param-that-will-be-on-all-requests-of-contracts": "some-value"
}
}
window.hapiApi.contract.setBaseConfig(yourBaseConfigForContracts)
Customizing per resource the URL, request method, headers, parameters and body, timeout etc.
Each API request function has some configuration available. We use axios package under the hood; thus, all available axios configs are available for you to change.
You can change these configs as such:
console.log(
'[HAPI][API/contracts/getContracts] config',
window.hapiApi.contract.configs.getContracts
)
// outputs:
{
url: "BASEURLOFCONTRACTS/contracts/"
method: "get"
}
const yourCustomConfigForGetContracts: AxiosRequestConfig<any> = {
method: 'post',
params: {
"Custom-Param": "param-value"
},
headers: {
"Custom-Header": "header-value"
},
data: {
someCustomData: {
"something": "data-value"
}
},
transformResponse: [
(data: any) => {
data.something = "some-value"
return data
}
]
}
window.hapiApi.contract.setResourceConfig("getContracts", yourCustomConfigForGetContracts)
The order of the API configurations matters. The execution order is as follows:
window.hapiApi.setBaseURL("URL")
window.hapiApi.setBaseConfig(CONFIG)
window.hapiApi.contract.setBaseURL("DIFFERENT URL")
window.hapiApi.contract.setBaseConfig(CONTRACT CONFIG)
window.hapiApi.contract.setResourceConfig(NAME OF API FUNCTION, CUSTOM CONFIG PARTICULAR TO THIS FUNCTION)
So, in other words, if you try to do window.hapiApi.setBaseURL
after you set the resources, then the resource URLs would get overwritten.
Using TypeScript types package for API Submodule
We recommend that you use the global approach, as you won't need to do the API-only types approach:
Global approach
Read more about how to set types globally here
API-only types approach
- Open Playground API Submodule of a module here
- On the very first few lines (most of the time second line), there is an import to the
api.types.ts
, jump to that file by CTRL (or CMD on Mac) + Click - Search by CTRL (or CMD on Mac) + F and type the name of the function
createContract
and you will find it in the respective type of the utilities submodule, in this case,WindowHapiAPIContractRequests
and the type is(contract: ContractCreateForm) => Promise<Contract>
import {
ContractCreateForm,
Contract
} from "@vonq/hapi-elements-types"
const createContractAPI = window.hapi.contract.api.createContract as (contract: ContractCreateForm) => Promise<Contract>
// run the function
await createContractAPI(
//...the args expected in the type
)
For list of available APIs, check modules in the Playground.