Extending a Module
In most cases, a module exports the base set of communication methods and utilities.
However, developers might require additional functionality that the module doesn't provide.
To address this issue without requesting new features from the module's author, each module can be customized to meet the specific needs of developers.
A module extension is an object that defines a custom behavior for a module. It can contain interceptors, utility methods, and subscribers. This enables developers to tailor the module to their specific needs and add new functionality.
SDK Core package exports the Extension
type that defines the structure of the module extension.
Example extension looks like this:
// SAPCC Example
import { Extension } from '@vue-storefront/sdk';
/**
* SAPCC Extension type.
* In this example we used `any` type for the `interceptors`, `utils`, `extend` and `override` properties, however, it's recommended to use a specific type for each of them.
*/
interface SAPCCExtension extends Extension {
interceptors: any[];
utils: any;
extend: any;
override: any;
subscribers: any;
}
export const sapccExtension: SAPCCExtension = {
interceptors: [],
utils: {},
extend: {},
override: {},
subscribers: {}
};
Let's review each of the extension properties to understand their roles and responsibilities.
Interceptors
Interceptors are functions that modify the input parameters of a method or the output result of a method.
before
interceptors
before
interceptors allow you to define a list of interceptors that can modify the input parameters of an SDK method.
These interceptors will run before your method call and will modify the input parameters before they enter the SDK method.
before
interceptors should not change the return type of the parameter!
The idea of before
interceptors is to modify the input parameters values only.
It should not be used to change the contract, that may break the typing and cause unforeseen issues.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
interceptors: [
{
before: {
getProducts: (args: Parameters<SAPCCModuleType['connector']['getProducts']>): Parameters<SAPCCModuleType['connector']['getProducts']> => {
console.log(`Interceptor modifies the input of getProducts method.`)
return [{
id: 2
}]
}
}
}
]
}
after
interceptors
after
interceptors allow you to define a list of interceptors that can modify the output of an SDK method.
These interceptors run after your method call and modify the output result.
after
interceptors should not change the return type of the parameter!
The idea of after
interceptors is to modify the output value only.
It should not be used to change the contract, that may break the typing and cause unforeseen issues.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
interceptors: [
{
after: {
getProducts: (res: ReturnType<SAPCCModuleType['connector']['getProducts']>): ReturnType<SAPCCModuleType['connector']['getProducts']> => {
console.log(`Interceptor modifies the output of getProducts method.`)
return [{ id: res[0].id, name: 'Hello world' }]
}
}
}
]
}
utils
The utils
property allows you to define methods that can be used to extend the module's functionalities. Utils should not depend on on any other components
Why to use utils
methods?
Imagine you're creating an integration with a payment provider.
To initialize the payment, you need to pass a specific config, that might be hard to create for someone who is not familiar with the payment provider.
In this case, you can create a utils
method that will create the config for you.
Such method won't be asynchronous and won't be affected by the interceptors.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
utils: {
buildConfig: (config: any) => {
return {
...config,
paymentServiceProvider: 'SAPCC-Payments'
};
}
}
};
Example of using utils
method:
// SAPCC Example
import { sdk } from './sdk'
// Using utils methods
const sapccPaymentConfig = sdk.sapcc.utils.buildConfig(baseConfig);
extend
extend
can be used to create a new method that is not covered by the module.
These methods are affected by interceptors
Like the built-in SDK methods, methods in extend
are impacted by your interceptors.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
extend: {
getProductBySku: (sku: string) => {
return axios.get(`/products/${sku}`);
}
}
};
Example of using extend
method:
// SAPCC Example
import { sdk } from './sdk';
// Using extend methods
const product = await sdk.sapcc.getProductBySku('product-sku');
override
While extend
allows you to create a new method, override
allows you to change the behavior of the existing method.
These methods are affected by interceptors
Like the built-in SDK methods, methods in extend
are impacted by your interceptors.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
override: {
getProducts: (params: any) => {
return axios.get(`/products`, { params });
}
}
};
Example of using override
method:
import { sdk } from './sdk';
// Using module's method
sdk.sapcc.getProducts({ ids: [1, 2, 3] });
subscribers
Subscribers are functions that are called when the specific event is emitted.
Events that can be emited are:
*_before
- run the function before EACH method of EACH module,*_after
- run the function after EACH method of EACH module,<module>_before
- run the function before EACH method of the specific module,<module>_after
- run the function after EACH method of the specific module,<module>_<method>_before
- run the function before the specific method of the specific module,<module>_<method>_after
- run the function after the specific method of the specific module.
It implements the publish-subscribe pattern.
It's a great place to add some custom logic, like logging or analytics.
// SAPCC Example
export const sapccExtension: SAPCCExtension = {
subscribers: {
sapcc_before: () => {
console.log(`Before each SAPCC method do something`);
},
sapcc_after: () => {
console.log(`After each SAPCC method do something`);
}
}
};
Using the extension
To use the extension, you need to import it and pass it as the third argument to the buildModule function. In addition, you need to pass the type of the extension (SAPCCExtensionType) as the second generic argument to the buildModule function. To pass the type of the extension, you can use the typeof operator. Here's an example:
// SAPCC Example
import { sapccModule, SAPCCModuleType } from '@vsf-enterprise/sapcc-sdk';
import { initSDK, buildModule } from '@vue-storefront/sdk';
import { sapccExtension, type SAPCCExtensionType } from './sapccExtension';
const sdkConfig = {
sapcc: buildModule<SAPCCModuleType, SAPCCExtensionType>(
sapccModule,
{
apiUrl: "http://localhost:8181/sapcc",
},
sapccExtension
),
};
export const sdk = initSDK<typeof sdkConfig>(sdkConfig);
Now you can use the extension in your application.