Extending API Methods
Vue Storefront integration for SAP Commerce Cloud ships with a default set of API methods responsible for direct communication with SAP's OCC API. It should be covering all of the most frequently used features of the platform. However, there might be scenarios where extending its capabilities will be required. Fortunately, in Vue Storefront it's easy to achieve with an integration extension (opens new window) and its extendApiMethods (opens new window) option.
Although the main Vue Storefront documentation describes the process pretty well, it does not mention the bits specific to SAP Commerce Cloud integration. This guide fills that gap and walks you through the process of creating a custom createCart()
API method which should:
- accept props coming from the frontend
- send a
POST
request to the SAP's /{baseSiteId}/users/{userId}/carts (opens new window) endpoint with the right parameters - return the response from SAP to the frontend
1. Creating an extension
Let us start by registering a new extension under sapcc
in middleware.config.js
. It should be an object with name
and extendApiMethods
properties. Don't worry about the latter being an empty object - we will define our custom API method there soon.
/* middleware.config.js */
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {}
};
module.exports = {
integrations: {
sapcc: {
// ...
extensions: existing => existing.concat(createCartExtension),
// ...
}
}
}
};
2. Defining a custom API method
With the extension in place, we can use its extendApiMethods
object to define our empty createCart()
method. It should be an async function accepting two arguments: context
and props
.
/* middleware.config.js */
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
+ createCart: async (context, props) => {
+
+ }
}
}
The context
argument gives us access to:
- the integration-specific configuration (including properties we've provided in
middleware.config.js
such asbaseSiteId
) - Axios (opens new window) instance used for communication between API Middleware and SAP OCC API
The props
argument is simply an object coming from the frontend. In the example below, it would be { fields: 'BASIC' }
.
import { onMounted } from '@nuxtjs/composition-api';
import { useVSFContext } from '@vue-storefront/core';
export default {
setup() {
const { $sapcc } = useVSFContext();
onMounted(async () => {
const cart = await $sapcc.api.createCart({ fields: 'BASIC' });
});
}
};
3. Creating the CartsApi
In simple terms, API methods are all about sending requests to SAP OCC API on behalf of the frontend. To simplify the process, Vue Storefront leverages CommerceWebservicesV2 (opens new window) - an SDK automatically generated by the Swagger Codegen project. It ships with various APIs and methods which make working with the OCC API a lot easier.
Good to know
A full list of APIs and their methods provided by CommerceWebservicesV2 (opens new window) can be found here (opens new window).
Vue Storefront provides its independent copy of the SDK as a separate package called @vsf-enterprise/sap-commerce-webservices-sdk
. You can import any desired API from there and use it in your custom API methods. In this example, we are going to create a new instance of the CartsApi
using some of the properties available in context
.
/* middleware.config.js */
+ const { CartsApi } = require('@vsf-enterprise/sap-commerce-webservices-sdk');
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
createCart: async (context, props) => {
+ const { client, config } = context;
+ const { sdkConfig, api: { uri } } = config;
+ const cartsApi = new CartsApi(sdkConfig, uri, client);
}
}
};
4. Creating a request with basic options
With the CartsApi
in place, we can use its createCart
method to:
- send the actual Axios request to SAP Commerce Cloud
- return the received data to the frontend
Looking at the createCart()
method interface, we can see it accepts a couple of arguments:
export declare class CartsApi extends BaseAPI {
// ...
createCart(
baseSiteId: string,
userId: string,
fields?: 'BASIC' | 'DEFAULT' | 'FULL',
oldCartId?: string,
toMergeCartGuid?: string,
options?: any
): Promise<import("axios").AxiosResponse<Cart>>;
}
Let's start with the low-hanging fruits. We are going to extract the values for baseSiteId
, oldCartId
and toMergeCartGuid
from context
and props
.
/* middleware.config.js */
const { CartsApi } = require('@vsf-enterprise/sap-commerce-webservices-sdk');
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
createCart: async (context, props) => {
const { client, config } = context;
+ const { sdkConfig, api: { uri, baseSiteId } } = config;
+ const { oldCartId, toMergeCartGuid } = props;
const cartsApi = new CartsApi(sdkConfig, uri, client);
+ const { data } = await cartsApi.createCart(
+ baseSiteId,
+ undefined,
+ undefined,
+ oldCartId,
+ toMergeCartGuid,
+ undefined
+ );
+
+ return data;
}
}
};
That was easy but what about the remaining undefined
arguments? Let's replace them with real values using some of the utility methods imported from @vsf-enterprise/sapcc-api
.
5. Adding userId
to request
To establish whether the request is sent by an anonymous or current (authenticated) user, we can use the getUserIdFromRequest()
method. It checks the req
object for the vsf-sap-token
cookie header. If the header is there, the method will return current. If it is not, it will return anonymous.
/* middleware.config.js */
const { CartsApi } = require('@vsf-enterprise/sap-commerce-webservices-sdk');
+ const { getUserIdFromRequest } = require('@vsf-enterprise/sapcc-api');
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
createCart: async (context, props) => {
+ const { client, config, req } = context;
const { sdkConfig, api: { uri, baseSiteId } } = config;
const { oldCartId, toMergeCartGuid } = props;
const cartsApi = new CartsApi(sdkConfig, uri, client);
const { data } = await cartsApi.createCart(
baseSiteId,
+ getUserIdFromRequest(req),
undefined,
oldCartId,
toMergeCartGuid,
undefined
);
return data;
}
}
}
6. Adding fields
to request
In the vast majority of cases, fields
will be included in the props
received from the frontend. In our custom API method, we only have to pass them on to cartsApi.createCart()
as an argument.
/* middleware.config.js */
const { CartsApi } = require('@vsf-enterprise/sap-commerce-webservices-sdk');
+ const { getUserIdFromRequest, createRequestFields } = require('@vsf-enterprise/sapcc-api');
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
createCart: async (context, props) => {
const { client, config, req } = context;
const { sdkConfig, api: { uri, baseSiteId } } = config;
const { oldCartId, toMergeCartGuid } = props;
const cartsApi = new CartsApi(sdkConfig, uri, client);
const { data } = await cartsApi.createCart(
baseSiteId,
getUserIdFromRequest(req),
+ createRequestFields({ props }),
oldCartId,
toMergeCartGuid,
undefined
);
return data;
}
}
}
In the above example, we are using the createRequestFields()
method. It extracts the fields
from props
and provides a fallback in case they are undefined
7. Adding options
to the request
The last argument expected by the cartsApi.createCart()
is options
. It might be the crucial one because it should include the Authorization
header and parameters such as lang
(language) and curr
(currency).
Appending the Authorization
header is particularly important since the request's success or failure might depend on it. The header should carry either:
- the application token (in case of anonymous users) or
- the customer token (in case of authenticated users)
Distributing access tokens inside API methods could use a separate article. However, in this tutorial, we are going to simplify things and use the createRequestOptions()
method. It does the heavy lifting for us and deals with both request parameters and the Authorization
header.
/* middleware.config.js */
const { CartsApi } = require('@vsf-enterprise/sap-commerce-webservices-sdk');
+ const { getUserIdFromRequest, createRequestFields, createRequestOptions } = require('@vsf-enterprise/sapcc-api');
const createCartExtension = {
name: 'create-cart-extension',
extendApiMethods: {
createCart: async (context, props) => {
const { client, config, req } = context;
const { sdkConfig, api: { uri, baseSiteId } } = config;
const { oldCartId, toMergeCartGuid } = props;
const cartsApi = new CartsApi(sdkConfig, uri, client);
const { data } = await cartsApi.createCart(
baseSiteId,
getUserIdFromRequest(req),
createRequestFields({ props }),
oldCartId,
toMergeCartGuid,
+ createRequestOptions({ context, props })
);
return data;
}
}
}
7. Testing the new API method
With the request options in place, our custom createCart()
method implementation is ready. We are now able to call it directly from any of our frontend components:
import { onMounted } from '@nuxtjs/composition-api';
import { useVSFContext } from '@vue-storefront/core';
export default {
setup() {
const { $sapcc } = useVSFContext();
onMounted(async () => {
const cart = await $sapcc.api.createCart({
fields: 'BASIC',
whatever: 'whatever'
});
});
}
};
If we set a breakpoint inside our custom method, it should pause the code execution at the selected line. This way we can double-check:
- the method gets called
- the method receives all expected props
- the method's inner logic behaves as expected