Release notes v1.10.0
Introduction
In this release we:
- enabled configuration of cookie options in
middleware.config.jsfile, - updated
@vue-storefront/coreand@vue-storefront/middlewaretov2.7.5.
Migration guide
To configure cookie options, you should modify your middleware.config.js file:
module.exports = {
integrations: {
ct: {
location: '@vsf-enterprise/commercetools-api/server',
configuration: {
+ cookie_options: {
+ 'vsf-commercetools-token': {
+ httpOnly: true,
+ secure: true,
+ sameSite: 'lax'
+ }
+ },
api: {
...
},
serverApi: {
...
},
currency: 'USD',
country: 'US',
languageMap: {
en: ['en', 'de'],
de: ['de', 'en']
}
},
...
}
}
};
By default, all cookie options are set to the most secure ones:
{
httpOnly: true,
secure: true,
sameSite: 'strict'
}
More information can be found in Localization and cookies guide.
Upgrading to 1.2.0
Introduction
This migration guide helps developers using our commercetools integration to upgrade to version 2.3.0.
For more information about this version, refer to the Overview page.
Changes
- Added E2E (cypress) tests:
- use
yarn test:e2eto start Cypress, - use
test:e2e:hlto run Cypress tests in headless mode. Useful for running the tests in CI/CD workflows, - use
test:e2e:generate:reportto generate Cypress report.
- use
- BREAKING Fixed helpers used for search on the Category page to read
phraseURL query string instead ofterm. - Added
SfLoadertoVsfShippingProviderto correctly handle the loading state. - Added expiration time to
vsf-commercetools-tokencookie matching token expiration time received from commercetools. Previously session cookies were used, which browsers delete when the current session ends. This forced users to log in more often than necessary. - Fixed an issue in the cart where API calls would result in
HTTP 413 Payload Too Largeerror code due to large payload size. Cart method calls will now only include cartidandversioninstead of the whole cart object.
We also made changes to the following files:
- updated
components/Checkout/CartPreview.vue, - updated
components/Checkout/UserBillingAddresses.vue, - updated
components/Checkout/UserShippingAddresses.vue, - updated
components/Checkout/VsfPaymentProviderMock.vue, - updated
components/Checkout/VsfShippingProvider.vue, - updated
components/MyAccount/BillingAddressForm.vue, - updated
components/MyAccount/PasswordResetForm.vue, - updated
components/MyAccount/ProfileUpdateForm.vue, - updated
components/MyAccount/ShippingAddressForm.vue, - updated
pages/Checkout/Billing.vue, - updated
pages/Checkout/Payment.vue, - updated
pages/Checkout/Shipping.vue, - updated
pages/Checkout/ThankYou.vue.
Upgrading to 1.2.0-rc.2
Introduction
This migration guide helps developers using our commercetools integration to upgrade to version 2.3.0-rc.2.
For more information about this version, refer to the Overview page.
Changes
We made changes to the following files:
- added new composable
useBilling, - added new composable
useShipping, - added new composable
useShippingProvider - added new component
components/Checkout/VsfShippingProvider, - added new component
components/Checkout/VsfPaymentProviderMock, - added new components
pages/Checkout/Billing.vue, - added new helper
helpers/Checkout/getShippingMethodPrice.ts, - added new middleware
middleware/is-authenticated.js, - added
middleware.config.js, - updated
components/Checkout/UserBillingAddresses.vue, - updated
components/Checkout/UserShippingAddresses.vue, - updated
composables/useUiHelpers/index.ts:- renamed
changeSearchTermtosetTermForUrl, - added
getSearchTermFromUrl,
- renamed
- updated translations in
theme/lang, - updated
middleware/checkout.js, - updated
pages/Checkout.vue, - updated
pages/Checkout/Payment.vue, - updated
pages/Checkout/Shipping.vue, - updated icons in
static/icons, - updated
nuxt.config.js, - updated
package.json, - deleted component
pages/Checkout/OrderReview.vue, - deleted component
pages/Checkout/PersonalDetails.vue.
Upgrading to 1.2.0-rc.3
Introduction
This migration guide helps developers using our commercetools integration to upgrade to version 2.3.0-rc.3.
For more information about this version, refer to the Overview page.
Changes
- Fixed mapping for product attributes with type
set. - Fixed cart getters to not throw errors when some properties are missing.
- Fixed error thrown by the Composition API plugin when importing
useWishlist.
We also made changes to the following files:
- updated
components/Checkout/VsfShippingProvider.vue, - deleted unused
packages/commercetools/theme/helpers/filters/getFiltersForUrl.js, - deleted unused
packages/commercetools/theme/helpers/filters/getFiltersFromUrl.js.
Upgrading to 1.3.0
Introduction
In 1.3.0 release, we introduce Forgot Password functionality and basic support for Stores and Channels in commercetools - you can now select a specific store and its corresponding channel, e.g., to display prices for a selected channel. We also fix some bugs related to Checkout forms and add new getters.
Changes
- BREAKING Enable the purchase of an item with selected supply channel and distribution channel
- Added
storeGetters,useStorecomposable and plugin for creating locale, currency, and country cookies - Added
forgotPasswordGettersanduseForgotPasswordcomposable - Added
getProductSkugetter toproductGetters - Extended
middleware.config.jswithinventoryModeoptional property - when added to the configuration, Inventory Mode is set on cart creation - Added smartphone only promo code input
- Removed hardcoded link to the category in
SearchResults.vue - Replaced mocked email address in MyProfile password change tab to active user email
- Fixed bug with input characters limits on Checkout forms preventing submitting the form with a street name less than five characters long and apartment number with less than two characters long
- Added e2e tests for Checkout
We also made changes to the following files:
- Added
pages/MyAccount/MyProfile.vue. - Updated
components/MyAccount/BillingAddressForm.vue. - Updated
components/MyAccount/ShippingAddressForm.vue. - Updated
pages/Checkout/Billing.vue. - Updated
pages/Checkout/Payment.vue. - Updated
pages/Checkout/Shipping.vue. - Updated
lang/de.jsandlang/en.js.
Upgrading to 1.3.2
Introduction
In the 1.3.2 release, we focused on improving security by limiting permissions given to the customers to the bare minimum. This release doesn't include breaking changes, but we HIGHLY recommend applying the changes described below to your projects.
Changes
In the past, all requests sent to the commercetools included customer's access token created with scopes defined in the middleware.config.js file. If the customer didn't have permission to access a given resource, commercetools would return an error. It sounds logical - you can't access or operate the data if you don't have permission.
Unfortunately, some operations require manage_* scopes. For example:
- resetting user password requires
manage_customersscope, - adding product reviews requires
manage_productsscope.
Giving these permissions to the customer would allow them to access and modify all customers and products in your commercetools project.
For this reason, we introduced a new key named serverApi to the commercetools middleware configuration. You can use it to pass new server-specific API client credentials that middleware will use instead of customers API client for selected operations that require manage_* permissions.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
api: {
uri: 'https://api.commercetools.com/PROJECT_KEY/graphql',
authHost: 'https://auth.sphere.io',
projectKey: 'PROJECT_KEY',
clientId: 'CLIENT_ID', // Customer API client ID
clientSecret: 'CLIENT_SECRET', // Customer API client secret
scopes: [
'create_anonymous_token:PROJECT_KEY',
'manage_my_profile:PROJECT_KEY',
'view_categories:PROJECT_KEY',
'manage_my_payments:PROJECT_KEY',
'manage_my_orders:PROJECT_KEY',
'manage_my_shopping_lists:PROJECT_KEY',
'view_published_products:PROJECT_KEY',
'view_stores:PROJECT_KEY'
]
},
serverApi: {
clientId: 'SERVER_ID', // Server API client ID
clientSecret: 'SERVER_SECRET', // Server API client secret
scopes: [
'manage_customers:PROJECT_KEY',
'manage_products:PROJECT_KEY'
]
}
}
}
}
};
API client used in the serverApi section can only have the manage_customers and manage_products scopes.
Alternatively, you can use just the manage_project scope, but keep in mind that this gives Alokai unlimited access to everything in your project. When dealing with permissions, less is always better ;)
Use two separate API clients for api and serverApi
While it's possible to use the same API client in api and serverApi, but will different scopes, we recommend creating two separate API clients.
Recommended customer scopes
The image below shows a list of recommended scopes for the API client configured in the api section should have.

Recommended server scopes
The image below shows a list of recommended scopes for the API client configured in the serverApi section should have.

Upgrading to 1.3.3
Introduction
In the 1.3.3 release, we added new options to the serverApi introduced in the 1.3.2 release.
Changes
In the 1.3.2 release, we introduced a new key named serverApi to the commercetools middleware configuration. It stores API client used to generate access tokens for selected operations. However, we quickly noticed the need to allow adding other operations that will use these access tokens. That's why in this release we added new operations option to the serverApi configuration.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
serverApi: {
clientId: 'SERVER_ID',
clientSecret: 'SERVER_SECRET',
scopes: [
'manage_customers:PROJECT_KEY',
'manage_products:PROJECT_KEY'
],
operations: []
}
}
}
}
};
Custom operations might require additional scopes
Remember that custom operations added to the operations array might require additional scopes.
Example
Let's assume you have custom GraphQL that adds new mutation like shown below:
mutation AddProductType(
$draft: ProductTypeDraft!
) {
productType: createProductType(draft: $draft) {
name
description
key
}
}
In this case, you need to add createProductType to the operations array:
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
serverApi: {
operations: [
'createProductType'
]
}
}
}
}
};
Upgrading to 1.4.0
Introduction
Start by updating the core packages
This guide only includes commercetools-specific changes. Follow the Migrating projects to 2.5.0 guide to update the core before you proceed with applying changes from this guide.
In this release, we focused on improving security and performance and updating GraphQL types.
This release includes some breaking changes. However, adjusting your projects shouldn't take too long and only requires a few minor changes.
Updated GraphQL types
You might have noticed that the @vue-storefront/commercetools-api package exports TypeScript types for GraphQL data returned by commercetools. We didn't update them for quite a while and decided that it was time to do it. There are plenty of new types as well as some breaking changes to the old types. Depending on the types you use, you might need to update your project.
Handling access tokens
We completely rewrote internals for handling authentication because of the poor performance of the old solution. Previously, the application requested a new access token several times for every user visiting the page if they didn't have the token cookie. This approach didn't make much sense, especially because all guests share the same access token until their session become unique in any way, for example, by logging in or adding an item to the cart or wishlist.
From now on, the application will request one access token for server operations (introduced in the previous release) and one for all guests. The server stores these tokens and doesn't send them out to the users. It will only create session cookies only for anonymous and logged-in users.
What is an anonymous session in commercetools?
You might be wondering what an "anonymous" session is and how it's different from the "guest" session. In commercetools there are three types of sessions:
- guest - user that didn't log in nor perform any visitor-specific operation,
- anonymous - user that didn't log in but performed some visitor-specific operation, for example, added item to the cart or wishlist,
- customer - user that logged into the application.
This change by itself doesn't require any changes in existing projects. However, we used this opportunity to improve the security of the cookies storing the user session. See the next section for more details.
Improving security of the session cookie
The commercetools integration creates the vsf-commercetools-token cookie to store the user session. In the past, this cookie didn't include the secure and httpOnly attributes. The lack of these attributes meant that the cookie could be sent with unsecured HTTP and be accessed using JavaScript, which exposed the cookie to security risks.
From now on, a session cookie will always have the httpOnly attribute. It will also have the secure attribute, but only if the request was HTTPS. For this reason, you don't have to use HTTPS in your development environments.
However, because the session cookie can no longer be accessed using JavaScript, we needed a new way of checking if the user is logged in. For this reason, we introduced a new isLoggedIn endpoint. You need to update the middleware/is-authenticated.js file, which protects /my-account routes from unauthorized access and all other parts of the application that read the session cookie.
// middleware/is-authenticated.js
import { Logger } from '@vue-storefront/core';
export default async ({ $vsf, route, redirect }) => {
try {
const isLoggedIn = await $vsf.$ct.api.isLoggedIn();
if (!isLoggedIn) {
throw new Error(`"${ route.fullPath }" route is only available to logged-in customers`);
}
} catch (error) {
Logger.warn(error.toString());
return redirect('/');
}
};
In components, you can use the same approach by using the useVSFContext helper to access the $ct context.
// Vue components
import { useVSFContext } from '@vue-storefront/core';
export default {
setup() {
const { $ct } = useVSFContext();
async function isLoggedIn() {
return await $ct.api.isLoggedIn();
}
}
};
Other changes
Below is the list of the template files that we updated since the last release. You can generate a new project using our CLI and compare the files listed below with your existing project to see if they need updating.
components/AppHeader.vue,components/CategoryPageHeader.vue,components/MyAccount/ProfileUpdateForm.vue,components/StoreLocaleSelector.vue,composables/useUiHelpers/index.ts,lang/de.js,lang/en.js,middleware/is-authenticated.js,nuxt.config.js,package.json,pages/Checkout.vue,pages/Checkout/Billing.vue,pages/Checkout/Payment.vue,pages/Checkout/Shipping.vue,pages/Checkout/ThankYou.vue,pages/MyAccount/OrderHistory.vue,pages/Product.vue.
If you are using E2E tests included in the projects, you might also need to update the following files.
tests/e2e/api/requests.ts,tests/e2e/fixtures/responses/getOrders.json,tests/e2e/fixtures/responses/getOrdersWithOffset.json,tests/e2e/fixtures/test-data/e2e-add-to-cart.json,tests/e2e/fixtures/test-data/e2e-carts-merging.json,tests/e2e/fixtures/test-data/e2e-my-account-order-history.json,tests/e2e/fixtures/test-data/e2e-product-page.json,tests/e2e/fixtures/test-data/e2e-remove-from-cart.json,tests/e2e/fixtures/test-data/e2e-update-cart.json,tests/e2e/integration/e2e-add-to-cart.spec.ts,tests/e2e/integration/e2e-carts-merging.spec.ts,tests/e2e/integration/e2e-checkout-order-summary.spec.ts,tests/e2e/integration/e2e-my-account-order-history.spec.ts,tests/e2e/integration/e2e-my-account.spec.ts,tests/e2e/integration/e2e-place-order.spec.ts,tests/e2e/integration/e2e-user-login.spec.ts,tests/e2e/integration/e2e-user-registration.spec.ts,tests/e2e/pages/category.ts,tests/e2e/pages/checkout.ts,tests/e2e/pages/components/breadcrumbs.ts,tests/e2e/pages/components/cart-sidebar.ts,tests/e2e/pages/components/header.ts,tests/e2e/pages/components/login-modal.ts,tests/e2e/pages/my-account.ts,tests/e2e/utils/network.ts.
Upgrading to 1.5.0
Introduction
In this release, we focused on improving the implementation of faceting. To be up-to-date with the latest features offered by commercetools, it no longer uses REST API but GraphQL API instead. It's also no longer a separate integration and is part of the commercetools integration.
We've also changed the organization's name under which we release the commercetools integration from @vue-storefront to @vsf-enterprise.
This release includes some breaking changes. However, adjusting your projects shouldn't take too long and only requires a few minor changes.
Update dependencies
Rename old dependencies
As stated above, we changed the organization's name under which we release commercetools integration. Open the package.json file and rename the following dependencies:
@vue-storefront/commercetoolsto@vsf-enterprise/commercetools@vue-storefront/commercetools-apito@vsf-enterprise/commercetools-api
Add new and remove old packages
Firstly add @vsf-enterprise/commercetools-api package (if it's not already installed).
yarn add @vsf-enterprise/commercetools-api
Starting this release, we require a new dependency:
vue-lazy-hydrationin version2.0.0-beta.4.
We also no longer use the @vue-storefront/nuxt-theme dependency.
To update your project, run the following commands:
yarn add vue-lazy-hydration@~2.0.0-beta.4
yarn remove @vue-storefront/nuxt-theme
Update configuration files
middleware.config.js
- Open
middleware.config.jsfile and replace@vue-storefront/commercetools-api/serverwith@vsf-enterprise/commercetools-api/serverfor location insidectsection.
// middleware.config.js
module.exports = {
integrations: {
ct: {
- location: '@vue-storefront/commercetools-api/server',
+ location: '@vsf-enterprise/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
}
},
}
};
- Remove
extensionsline fromctsection.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vsf-enterprise/commercetools-api/server',
- extensions: existing => existing.concat('@vsf-enterprise/commercetools/extensions'),
configuration: {
// irrelevant configuration was omitted for readability
}
},
- Remove
ctfsection. It will no longer be used as faceting is now part of commercetools integration.
// middleware.config.js
module.exports = {
integrations: {
// ct integration config
- ctf: {
- location: '@vsf-enterprise/ct-faceting/server',
- configuration: {
- api: {
- authHost: 'https://auth.sphere.io',
- projectKey: '<PROJECT_KEY>',
- clientId: '<CLIENT_ID>',
- clientSecret: '<CLIENT_SECRET>',
- scopes: [
- /* scope rules */
- ]
- },
- faceting: {
- host: 'https://api.commercetools.com'
- }
- }
- }
}
};
nuxt.config.js
- Default configuration for faceting is no longer provided by external package
@vsf-enterprise/ct-faceting. Now it is placed innuxt.config.jsunderbuildModulessection and in@vsf-enterprise/commercetools/nuxtmodule. If you don't have default configuration or you need more details please refer to Configuration section of theuseFacetcomposable.
Context configuration access
Since we're removing $ctf object from the middleware configuration, and still referring to the application context with it, now all the faceting configuration data will be available from the $ct key/object.
Example:
// before
const { $ctf: { config }} = useVSFContext();
// now
const { $ct: { config: { faceting } }} = useVSFContext();
Please be aware that with defining facets your commercetools data structure might be different, and reaching for specific objects might need some adjustments.
- We no longer use the
@vue-storefront/nuxt-themepackage which registered default routing paths (we will remove it after). They need to be configured in thenuxt.config.jsfile.
To do it, use the extendRoutes function in the nuxt.config.js.
// nuxt.config.js
router: {
// other configuration options
extendRoutes(routes, resolve) {
routes.push({
name: 'home',
path: '/',
component: resolve(__dirname, 'pages/Home.vue')
},
{
name: 'product',
path: '/p/:id/:slug/',
component: resolve(__dirname, 'pages/Product.vue')
},
{
name: 'category',
path: '/c/:slug_1/:slug_2?/:slug_3?/:slug_4?/:slug_5?',
component: resolve(__dirname, 'pages/Category.vue')
},
{
name: 'my-account',
path: '/my-account/:pageName?',
component: resolve(__dirname, 'pages/MyAccount.vue')
},
{
name: 'checkout',
path: '/checkout',
component: resolve(__dirname, 'pages/Checkout.vue'),
children: [
{
path: 'shipping',
name: 'shipping',
component: resolve(__dirname, 'pages/Checkout/Shipping.vue')
},
{
path: 'billing',
name: 'billing',
component: resolve(__dirname, 'pages/Checkout/Billing.vue')
},
{
path: 'payment',
name: 'payment',
component: resolve(__dirname, 'pages/Checkout/Payment.vue')
},
{
path: 'thank-you',
name: 'thank-you',
component: resolve(__dirname, 'pages/Checkout/ThankYou.vue')
}
]
},
{
name: 'reset-password',
path: '/reset-password',
component: resolve(__dirname, 'pages/ResetPassword.vue')
});
}
},
Routes configuration can be moved to a separate file and imported into the nuxt.config.js file to increase the readability.
@vue-storefront/nuxt-themepackage needs to be removed frombuildModulessection.
buildModules: [
- ['@vue-storefront/nuxt-theme'],
- As faceting is now part of commercetools integration, the configuration inside
buildModulesis no longer needed and has to be removed.
buildModules: [
- ['@vsf-enterprise/ct-faceting/nuxt', {
- apiConfigModule: '@vsf-enterprise/commercetools/nuxt'
- }]
- Update configuration of the
@vue-storefront/nuxtmodule by replacing@vue-storefront/commercetoolswith@vsf-enterprise/commercetoolsinuseRawSourceobject.
['@vue-storefront/nuxt', {
useRawSource: {
dev: [
'@vue-storefront/core',
- '@vue-storefront/commercetools'
+ '@vsf-enterprise/commercetools'
],
prod: [
'@vue-storefront/core',
- '@vue-storefront/commercetools'
+ '@vsf-enterprise/commercetools'
]
}
}]
Update imports
The final step is to replace ALL @vue-storefront/commercetools imports with @vsf-enterprise/commercetools in *.vue template file.
- import { useUser } from '@vue-storefront/commercetools';
+ import { useUser } from '@vsf-enterprise/commercetools';
The same applies to @vue-storefront/commercetools-api, if it's used in the project. It has to be replaced with @vsf-enterprise/commercetools-api.
- import { Category } from '@vue-storefront/commercetools-api';
+ import { Category } from '@vsf-enterprise/commercetools-api';
Optional: Redis cache
In this version, we've added support for Redis cache out of the box for new projects. If you want to use it, please follow the instructions below. If not, you can skip this step.
You can read more about our implementation of Redis cache on SSR Cache page.
Add new packages
Start by installing required dependencies:
@vue-storefront/cachein version2.5.3@vsf-enterprise/redis-cachein version1.3.0
To update your project, run the following commands:
yarn add @vue-storefront/cache@~2.5.3
yarn add @vsf-enterprise/redis-cache@~1.3.0
Add body parser server middleware
Create a body-parser.js file inside of the serverMiddleware directory with the following code:
// ./serverMiddleware/body-parser.js
const bodyParser = require('body-parser')
const app = require('express')()
app.use(bodyParser.json())
module.exports = app
Add the location of the body-parser.js file to the serverMiddleware array in nuxt.config.js.
serverMiddleware: [
'~/serverMiddleware/body-parser.js'
],
Extend configuration
In the nuxt.config.js file, update the modules section by adding the @vue-storefront/cache/nuxt module with the following configuration.
modules: [
// other modules
['@vue-storefront/cache/nuxt', {
enabled: process.env.VSF_REDIS_ENABLED === 'true',
invalidation: {
endpoint: process.env.CACHE_INVALIDATE_URL,
key: process.env.CACHE_INVALIDATE_KEY,
handlers: [
'@vue-storefront/cache/defaultHandler'
]
},
driver: [
'@vsf-enterprise/redis-cache',
{
// docs: https://github.com/luin/ioredis/blob/master/API.md#new-redisport-host-options
redis: {
keyPrefix: process.env.VSF_REDIS_KEY_PREFIX,
host: process.env.VSF_REDIS_HOST,
port: process.env.VSF_REDIS_PORT || 6379,
password: process.env.VSF_REDIS_PASSWORD
}
}
]
}]
],
Update template file
As the last step, update template files that use useCache composable. Start by adding an import statement:
import { useCache, CacheTagPrefix } from '@vue-storefront/cache';
Then, use the useCache composable in the setup function to add proper tags. Below is a code example of handling tags on the Category page.
setup() {
const { addTags } = useCache();
onSSR(() => {
const tags = [{ prefix: CacheTagPrefix.View, value: 'category' }];
const productTags = products.value.map((prod) => {
return { prefix: CacheTagPrefix.Product, value: prod._id };
});
const categoriesTags = categoryTree.value.items.map((cat) => {
return { prefix: CacheTagPrefix.Category, value: cat.id };
});
addTags(tags.concat(productTags, categoriesTags));
});
}
Upgrading to 1.6.0
Introduction
In this release, we've migrated from the old internationalization package called nuxt-i18n to the new one called nuxtjs/i18n. Follow its Migration guide to upgrade your project. We've also refactored the useUserBilling and useUserShipping composables.
Updating the useUserBilling and useUserShipping implementations
The billing and shipping properties from the useUserBilling and useUserShipping composables reference the user object provided by the useUser composable.
For this reason, we are deprecating the load methods from the useUserBilling and useUserShipping composables. To populate all the billing and shipping properties with user data, call the useUser.load method.
import { onSSR } from '@vue-storefront/core'
import {
useUser,
useUserBilling,
useUserShipping
} from '@vsf-enterprise/commercetools';
export default {
setup() {
const { load: loadUser } = useUser();
- const { load: loadBilling, billing } = useUserBilling();
- const { load: loadShipping, shipping } = useUserShipping();
+ const { billing } = useUserBilling();
+ const { shipping } = useUserShipping();
onSSR(async () => {
await loadUser();
- await loadBilling();
- await loadShipping();
});
return {
billing,
shipping
};
}
};
Calling the useUserBilling.load and useUserShipping.load methods is no longer necessary and will result in a deprecation warning.
Upgrading to 1.7.0
Introduction
Hi. With this version, we're introducing some significant performance optimization like new improved typings, the new Node version or conditional rendering removal, finally build modern mode for client.
Upgrade to Node 12 or 14
We have recommended using Node 12 or higher for a few months now. It's mandatory starting with this release because @vsf-enterprise/commercetools-api and @vsf-enterprise/commercetools packages are now targeting ES2019 syntax, supported fully in Node 12+.
At this moment, we don't support Node versions higher than 14.
Update import paths of types generated from commercetools GraphQL API
You might have noticed that some of the types exported from the @vsf-enterprise/commercetools-api represent data accepted and returned from the commercetools GraphQL API. We thought it would be easier to differentiate our types from those generated from the API if they come from two different packages.
For this reason, we introduced the new @vsf-enterprise/commercetools-types package. It only contains types generated from the commercetools GraphQL API and nothing more.
The @vsf-enterprise/commercetools-api package only includes types for the Server Middleware API, such as parameters accepted and data returned from API endpoints and default GraphQL queries sent to the commercetools GraphQL API.
You can refer to the API Reference for more details.
- import type { Customer } from '@vsf-enterprise/commercetools-api`;
+ import type { Customer } from '@vsf-enterprise/commercetools-types`;
Update deprecated types
We refactored most types in @vsf-enterprise/commercetools-api and @vsf-enterprise/commercetools packages to improve coding suggestions. We marked most of the types we no longer use as deprecated with comments indicating the new type that you should use instead. Most IDEs should highlight deprecated types — you can click them to go to the type declaration file for more information.
We will remove deprecated types in version 1.7.0.
Upgrade @storefront-ui/vue package to version 0.13.0
With the new Storefront UI version, we bring some performance improvements however, you need to make some changes to your components.
# components/CartSidebar
<SfSidebar
class="sf-sidebar--right"
v-e2e="'sidebar-cart'"
:visible="isCartSidebarOpen"
:title="$t('My Cart')"
@close="toggleCartSidebar"
:persistent="hasNotifications"
+ position="right"
>
# components/WishlistSidebar
<SfSidebar
class="sf-sidebar--right"
v-e2e="'sidebar-wishlist'"
:visible="isWishlistSidebarOpen"
:title="$t('My wishlist')"
@close="toggleWishlistSidebar"
:persistent="hasNotifications"
+ position="right"
>
Remove mobileObserver
With this version we're getting rid of all the conditional rendering. This means that we're not using mobileObserver utility anymore. It was removed along with the new Storefront UI version.
Please follow these steps to cover all the necessary changes. This contributes to our full CDN optimization.
# components/AppFooter
data() {
return {
- isMobile: false,
- desktopMin: 1024
}
}
# components/AppHeader
<template #navigation>
- <HeaderNavigation :isMobile="isMobile">
+ <HeaderNavigation>
</template>
...
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- const isMobile = ref(mapMobileObserver().isMobile.get());
...
watch(() => term.value, (newVal, oldVal) => {
- const shouldSearchBeOpened = (!isMobile.value && term.value.length > 0) && ((!oldVal && newVal) || (newVal.length !== oldVal.length && isSearchOpen.value === false));
- if (shouldSearchBeOpened) {
+ if (term.value.length > 0 && ((!oldVal && newVal) || (newVal.length !== oldVal.length && isSearchOpen.value === false))) {
isSearchOpen.value = true;
}
});
...
- onBeforeUnmount(() => {
- unMapMobileObserver();
- });
...
return {
...
- isMobile
}
# components/Headernavigation
<template>
- <div class="sf-header__navigation desktop" v-if="!isMobile">
- <SfHeaderNavigationItem
- v-for="category in categories"
- :key="category.id"
- class="nav-item"
- v-e2e="`app-header-url_${category.slug}`"
- :label="category.name"
- :link="localePath(`/c/${category.slug}`)"
- />
- </div>
- <SfModal v-else :visible="isMobileMenuOpen">
+ <div class="header-navigation">
+ <div class="sf-header__navigation desktop-only">
+ <SfHeaderNavigationItem
+ v-for="category in categories"
+ :key="category.id"
+ class="nav-item"
+ v-e2e="`app-header-url_${category.slug}`"
+ :label="category.name"
+ :link="localePath(`/c/${category.slug}`)"
+ />
+ </div>
+ <SfModal class="smartphone-only" :visible="isMobileMenuOpen">
<SfList>
<SfListItem
v-for="category in categories"
:key="category.id"
class="nav-item sf-header-navigation-item"
v-e2e="`app-header-url_${category.slug}`"
>
<SfMenuItem
:label="category.name"
class="sf-header-navigation-item__menu-item"
:link="localePath(`/c/${category.slug}`)"
@click.native="toggleMobileMenu"
/>
</SfListItem>
</SfList>
</SfModal>
+ </div>
</template>
...
- props: {
- isMobile: {
- type: Boolean,
- default: false
- }
- },
# components/InstagramFeed
<template>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageAm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageAd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageAm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageAd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageBm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageCd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageBm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageCd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageCm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageBd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageCm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageBd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageDm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageDd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageDm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageDd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
</template>
...
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- computed: {
- ...mapMobileObserver()
- },
- beforeDestroy() {
- unMapMobileObserver();
- }
# pages/MyAccount
- import { computed, onBeforeUnmount, useContext, useRouter, useRoute } from '@nuxtjs/composition-api';
+ import { computed, useContext, useRouter, useRoute } from '@nuxtjs/composition-api';
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- const isMobile = computed(() => mapMobileObserver().isMobile.get());
const activePage = computed(() => {
const { pageName } = route.value.params;
if (pageName) {
return (pageName.charAt(0).toUpperCase() + pageName.slice(1)).replace('-', ' ');
}
- else {
- return !isMobile.value ? i18n.t('My profile') : '';
- }
});
- onBeforeUnmount(() => {
- unMapMobileObserver();
- });
Use --modern=client mode
From now on, with application build we're using Nuxt modern mode just for client side. More about this you can find here.
# package.json
- "build": "nuxt build -m",
- "build:analyze": "nuxt build -a -m",
- "start": "nuxt start",
+ "build": "nuxt build --modern=client",
+ "build:analyze": "nuxt build -a --modern=client",
+ "start": "nuxt start --modern=client",
Upgrading to 1.8.5
Introduction
In the 1.8.5 release, we changed the response of productsSearch to return the raw data whenever customQuery is passed as parameter.
Changes
Previously, productsSearch needed the enhanceProduct parameter to handle customQuery responses but this fail to work due to the implementation of the productsSearch method that returns enhancedProductResponse.data.
return (enhancedProductResponse.data as any)._variants;
This would fail if the enhanceProduct parameter did not properly format the data needed to create the response of the productsSearch which is not documented correctly.
With this we created this change to directly return the raw response coming from context.$ct.api.getProduct(<searchParams>, <customQuery>)
The change will not require everyone to pass in enhanceProduct and just allow them to modify or process the response after they received it.
Usage
Before
Previously, enhanceProduct was part of productsSearch and modify the data prior to being returned.
const { search, products } = useProduct()
// Which will fail cause of the required format of response of enhanceProduct
await search({
customQuery: { products: 'my-custom-query' },
enhanceProduct: (productResponse) => productResponse
})
Now
Now, since the productsSearch will now return raw response whenever there's a customQuery present. You may be able to add a method like enhanceProduct below that can transform the raw response to what's needed.
const { search, products } = useProduct()
const enhanceProduct = (productResponse) => productResponse
await search({
customQuery: { products: 'my-custom-query' }
})
products.value = enhanceProduct(products.value)
Upgrading to 1.9.0
Introduction
The main and the most important change was implementation of the init function for a performance boost and rework of
the token flow logic, which was a necessary step after the usage of the init function.
Init function
Previously, Apollo Client was created on every request which was not a very good solution in terms of performance.
Utilizing the new core mechanism we implemented the init function to bootstrap the client once on the build time.
| File | What was changed |
|---|---|
| ! packages/api-client/src/index.server.ts | Move client creation logic from the onCreate to the init function |
For a reference please check getInitConfig in the middleware package.
Token flow
Due to tokens leaks and security issues after the implementation of the init function, token flow was reworked.
| File | What was changed |
|---|---|
| ! packages/api-client/src/token/TokenManagerFactory.ts | Creates object with helpers for reading and creating access token cookies and accessing current token provider |
| ! packages/api-client/src/token/tokenParser.ts | Parses raw token string into an object |
| ! packages/api-client/src/links/** | All Apollo Client links related factories and handlers |
| ! packages/api-client/src/links/createLinks.ts | Main entry point for links creation |
Token Flow Diagrams




