Multistore
Since version 2.0.0, Vue Storefront's integration for SAP Commerce Cloud allows you to work with multiple Base Sites and Base Stores within a single application instance.
Good to know
Before you follow this guide, we strongly recommend you read the official SAP Commerce Cloud documentation on Base Sites and Base Stores.
Assumptions
We have created our multistore implementation based on the standard Base Site & Base Store setup provided by SAP Comerce Cloud:
- the electronics Base Site featuring the electronics Base Store and the electronicsProductCatalog
- the apparel-uk Base Site featuring the apparel-uk Base Store and the apparelProductCatalog
Configuration
Multistore configuration is pretty much the same as the standard one described in the Configuration section of our docs. There are, however, a few minor differences to keep in mind.
Good to know
Whenever you change some store settings (e.g. defaultCurrency
) in your SAP Backoffice, you will need to re-build and re-deploy your application.
Middleware configuration
To support two or more SAP stores, you should use the VSF multi-store extension.
- First, add a multi-store extension to your middleware.config.js file
# middleware.config.js
require('dotenv').config();
+ const { multistoreExtension } = require('@vsf-enterprise/multistore');
module.exports = {
integrations: {
sapcc: {
location: '@vsf-enterprise/sapcc-api/server',
+ extensions: (extensions) => [
+ ...extensions,
+ multistoreExtension
+ ],
configuration: {
OAuth: {
uri: process.env.SAPCC_OAUTH_URI,
clientId: process.env.SAPCC_OAUTH_CLIENT_ID,
clientSecret: process.env.SAPCC_OAUTH_CLIENT_SECRET,
tokenEndpoint: process.env.SAPCC_OAUTH_TOKEN_ENDPOINT,
tokenRevokeEndpoint: process.env.SAPCC_OAUTH_TOKEN_REVOKE_ENDPOINT,
cookieOptions: {
'vsf-sap-token': { secure: process.env.NODE_ENV !== 'development' }
}
},
api: {
uri: process.env.SAPCC_API_URI,
baseSiteId: 'electronics',
catalogId: 'electronicsProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'USD'
}
}
}
}
};
At this point, configuration.api
contains the default store configuration.
- Create a
multistore.config.js
file with the following methods:
fetchConfiguration({ domain }): Record<string, StoreConfig>
- fetches store configuration. The method accepts the domain as an argument and returns with the store-specific configuration based on the domains where the domain is a key and the configuration is a value.mergeConfigurations({ baseConfig, storeConfig }): StoreConfig
- overwrites base configuration with store-specific config.cacheManagerFactory(): { get: (key: string) => StoreConfig, set(key: string, value: StoreConfig)}
- creates cache manager withget
andset
methods.
This file could look like the example below. It uses node-cache package as a cache manager.
// multistore.config.js
const NodeCache = require('node-cache');
module.exports = {
fetchConfiguration({ domain }) {
return {
'apparel.mystore.io': {
baseSiteId: 'apparel-uk',
catalogId: 'apparelProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'GBP'
},
'electronics.mystore.io': {
baseSiteId: 'electronics',
catalogId: 'electronicsProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'USD'
},
'sap.localhost.apparel:3000': {
baseSiteId: 'apparel-uk',
catalogId: 'apparelProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'GBP'
},
'localhost:3000': {
baseSiteId: 'electronics',
catalogId: 'electronicsProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'USD'
}
};
},
mergeConfigurations({ baseConfig, storeConfig }) {
return {
...baseConfig,
api: {
...baseConfig.api,
...storeConfig
}
};
},
cacheManagerFactory() {
const client = new NodeCache({
stdTTL: 10
});
return {
get(key) {
return client.get(key);
},
set(key, value) {
return client.set(key, value);
}
};
}
};
- Add multi-store configuration to the
middleware.config.js
file.
# middleware.config.js
require('dotenv').config();
+ const multistore = require('./multistore.config');
const { multistoreExtension } = require('@vsf-enterprise/multistore');
module.exports = {
integrations: {
sapcc: {
location: '@vsf-enterprise/sapcc-api/server',
extensions: (extensions) => [
...extensions,
multistoreExtension
],
configuration: {
OAuth: {
uri: process.env.SAPCC_OAUTH_URI,
clientId: process.env.SAPCC_OAUTH_CLIENT_ID,
clientSecret: process.env.SAPCC_OAUTH_CLIENT_SECRET,
tokenEndpoint: process.env.SAPCC_OAUTH_TOKEN_ENDPOINT,
tokenRevokeEndpoint: process.env.SAPCC_OAUTH_TOKEN_REVOKE_ENDPOINT,
cookieOptions: {
'vsf-sap-token': { secure: process.env.NODE_ENV !== 'development' }
}
},
api: {
uri: process.env.SAPCC_API_URI,
baseSiteId: 'electronics',
catalogId: 'electronicsProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'USD'
},
+ multistore
},
}
}
};
- That's all. Now, based on the domain, the middleware will know how to overwrite the config to use a store-specific one to perform proper SAPCC API requests.
Local development
The simplest way to work with a multistore setup locally is by adding new domains to the /etc/hosts
file:
127.0.0.1 localhost
127.0.0.1 sap.localhost.apparel
127.0.0.1 sap.localhost.electronics
This way you will be able to access your application - running by default on localhost:3000
- with different domains (e.g. sap.localhost.apparel:3000
). Remember to add store-specific config to your multistore.config.js
.
Flow breakdown
As mentioned in the Configuration section, Vue Storefront uses a simple domain detection mechanism to load the desired store configuration and serve store-specific data to the users. The diagram below presents a sequence of events happening on the initial application load before the first Vue component is mounted.
As you can see, the flow features two main actors: LoadStore plugin on the front end and the Store Service in the API Middleware. The former is executed only once during the first application load. The latter is used for every request to ensure they are sent with the right baseSiteId
. This allows Vue Storefront to serve store-specific data to the users.