Migrate to Storefront v8
This guide provides a comprehensive step-by-step process for migrating your Storefront Application-based application utilizing the SCAYLE Storefront SDK packages (storefront-core & storefront-nuxt) from version 7 to version 8. This major release marks a significant milestone in the evolution of SCAYLE Storefront, as we've undertaken a substantial architectural overhaul throughout the year. With the arrival of version 8, we're taking the opportunity to streamline the SDK packages, removing legacy code and deprecations, while also sunsetting support for outdated approaches like faceted search (e.g. useFacet). This migration guide will act as your roadmap, ensuring a smooth transition to the enhanced performance, stability, and developer experience that SCAYLE Storefront v8 offers.
The complexity of your migration from SCAYLE Storefront v7 to v8 will depend significantly on the initial version of the SCAYLE Storefront Application used as the foundation for your project and update frequency. While projects consistently updated with the latest Storefront SDK versions will experience a smoother transition, those based on older versions, particularly Release Candidate (RC) versions of the SCAYLE Storefront Application, will require additional effort. This may involve manually incorporating architectural changes introduced in subsequent Boilerplate releases. Please assess your project's starting point to accurately estimate the migration scope and resources required.
A Short Overview of Breaking Changes
- 💥 Unified Storage Configuration: Legacy Storage Configuration Removed
- 💥 Migrate from
getBadgeLabel: Implement Custom Badge Logic - 💥 Migrate to
shopsConfiguration: Replacingstore - 💥 Simplified Key Handling in RPC Composables:
keyvalue migrated to dedicated parameter - 💥 Updating API Configuration References: Transitioning from
bapitosapi - 💥 Streamlining Cache Control: Replacing
AY_CACHE_DISABLEDwithstorefront.cache.enabled - 💥 Migrating to Enhanced Session Management: Now Opt-In
- 💥 Accessing Basket Data: New Response Structure and Error Handling
- 💥 Streamlining Data Fetching parameter: Transitioning to
immediateand removingautoFetch - 💥 Accessing User Access Tokens: Migrating to the getAccessToken RPC
- 💥 Standardized Error Handling in RPC Methods:
BAPIError&BaseErrorRemoved - 💥 Simplified API Access in Multi-Shop Setups: Global API Routing for Path-Based Shops
- 💥 Search v1 replaced by Search v2: Removing
searchProductsRPC in favor ofgetSearchSuggestions - 💥Aligning with Nuxt
useAsyncData: Replacing return valuespendingwithstatusandfetchwithrefresh - 💥 Renaming of
useNavigationTree: Now useNavigationTreeById - 💥 Removed Composable
useFacet: Using dedicated Category-Specific Composables - 💥 Removed Composable
useQueryFilterState: Improving Filter Management withuseFilteranduseAppliedFilters - 💥 Simplified IDP Integration: Using
loginIDPfor Callback Handling - 💥 RpcContext Cleanup: Removing Legacy Attributes
- 💥 Enhanced Key Security: Using SHA256 for Basket and Wishlist Key Generation
- 💥 Simplified Composable Caching:
enableDefaultGetCachedDataOverrideReplacesdisableDefaultGetCachedDataOverride - 💥 Migrating User Authentication: Removing deprecated
loginShopIdAttribute
Unified Storage Configuration: Legacy Storage Configuration Removed
We've streamlined the storage configuration process in v8. The deprecated transformLegacyConfig function has been removed, replaced by the new storefront.storage configuration system.
What this means for you:
- Legacy configuration options
providerandredisconfiguration are no longer supported and had been deprecated since Storefront Application v1.0.0-rc.05. - You'll need to migrate your existing storage settings to the new
storefront.storageformat.- Check Storefront Guide ➜ Developer Guide ➜ Basic Setup ➜ Introduction - Storage for additional information.
These changes might impact your environment variables used for deployments. Please check your infrastructure and deployment setup and adapt accordingly!
Here's a quick example to guide you:
No Longer Supported: Legacy Storage Setup (nuxt.config.ts):
Old Environment Variables (used during runtime) / .env:
Migrate from getBadgeLabel: Implement Custom Badge Logic
Starting with v8, we're empowering you to have more control over the display of badge labels within your applications.
What's changing?
- The
getBadgeLabelhelper function is no longer exported. - You will now implement the logic for generating your badge labels directly within your application code.
A detailed example of creating a "sustainability" badge can be found in our documentation: Getting Started ➜ Quick Start ➜ Create a Sustainability Badge.
Good News for Some: If your project uses the SCAYLE Storefront Application v1.0 or later, this change won't affect you, as the getBadgeLabel function was already removed in that release.
For users with projects based on older boilerplate versions, or those directly utilizing getBadgeLabel, you can refer to the previous implementation below as a reference for your custom implementation:
Reference Implementation: getBadgeLabel
💥 Migrate to shops Configuration (Replacing store)
We're streamlining how you configure multiple countries in v8. While we've been letting the old store configuration option hang around for compatibility, it's time to fully embrace its future proof successor: shops!
What's changing?
- The
storeoption in your module configuration is officially retired. - You'll now use the
shopskeyword to configure your storefront settings.
This transition to the shops configuration offers a more robust and scalable solution for handling multiple storefronts. You may already be familiar with this change as it was initially introduced in v7.84.0 (released 1. August 2024) on to ensure a smooth migration path.
These changes might impact your environment variables used for deployments. Please check your infrastructure and deployment setup and adapt accordingly!
Need a helping hand with the new configuration? Our documentation has you covered: Storefront Guide / Developer Guide / Basic Setup / Introduction - Shops
Here's a quick example to guide you:
nuxt.config.ts:
New Environment Variable structure (used during runtime) / .env:
💥 Simplified Key Handling in RPC Composables: key value migrated to a dedicated parameter
To better align with the current Nuxt 3 architecture and make key handling more explicit, we've simplified how you interact with the key parameter in RPC composables from version 8 onwards.
What's changing?
- Key as a Direct Argument: Instead of placing the
keywithin the composable'soptionsobject, you'll now provide it as the second argument when calling the composable.
This adjustment promotes consistency and improves code readability by making the key's purpose more explicit.
Here's a quick example to guide you:
💥 Updating API Configuration References: Transitioning from bapi to sapi
We've updated our configuration to use sapi (shorthand for Storefront API) consistently, replacing the deprecated bapi keyword for improved clarity and consistency.
What's changing?
- All instances of
bapiin the codebase have been replaced withsapi. - The
initBapifunction has been removed and is no longer available. - The
bapiClientproperty been replaced withsapiClientwithin theRPCContext.
These changes impact your environment variables used for deployments. Please check your infrastructure and deployment setup and adapt accordingly.
To make sure your projects are compatible with the latest version, we recommend updating your codebases as shown in the example:
nuxt.config.ts:
Legacy Environment Variables (used during runtime) / .env:
💥 Streamlining Cache Control: Replacing AY_CACHE_DISABLED with storefront.cache.enabled
We've simplified the way you manage caching behavior in v8 by replacing the legacy AY_CACHE_DISABLED environment variable with more intuitive options.
What's changing?
- The
AY_CACHE_DISABLEDenvironment variable is no longer supported. - You can now control cache behavior using either of the following:
- The
NUXT_STOREFRONT_CACHE_ENABLEDenvironment variable. - The
storefront.cache.enabledoption within yournuxt.config.tsfile.
- The
These changes provide greater flexibility and clarity when configuring your cache settings.
Environment Variables (used during runtime) / .env:
💥 Migrating to Enhanced Session Management: Now Opt-In
We've improved session management for multi-storefront setups in @scayle/storefront-nuxt to provide greater stability and ease of implementation.
What's changing?
- Starting with version
@scayle/[email protected](released on 13 May 2024), each shop utilizes a unique session cookie name instead of relying on thePathattribute. This simplifies handling multiple storefronts and enhances session security. - The automatic migration of legacy session data introduced in
@scayle/[email protected](released on 13 May 2024) has been disabled by default and now need to have a feature flagstorefront.legacy.enableSessionMigrationenabled.
Storefront Config: storefront.legacy.enableSessionMigration
In nuxt.config.ts:
Important considerations for upgrading and ensuring a smooth transition:
Direct upgrades from versions prior to @scayle/[email protected] (released on 13 May 2024) will result in the loss of user sessions without enabling the storefront.legacy.enableSessionMigration feature flag.
- Deploy version
@scayle/[email protected](released on 13 May 2024) or higher in your production environment or alternatively deploy version@scalye/[email protected]or higher while enablingstorefront.legacy.enableSessionMigration. - Keep this version running for a period exceeding your configured session TTL (time-to-live). This allows all legacy session data to migrate.
- Once the session TTL period has passed, you can safely disable
storefront.legacy.enableSessionMigration
Cookie Format Changes: This change primarily affects internal session management. However, for reference, here's a comparison of the old and new cookie formats:
Before @scayle/[email protected] (released on 13 May 2024):
💥 Accessing Basket Data: New Response Structure and Error Handling
This update changes how you interact with the basket through our API. Instead of receiving the basket object directly from certain methods, it will now be nested within the response body. We've also standardized error handling for adding items and improved how our useBasket composable handles partial successes.
What's changing?
- The following methods no longer return the basket object directly:
getBasketremoveItemFromBasketaddItemsToBasketaddItemToBasket
- The basket object will now be accessible as a property within the response body of these methods.
addItemToBasketandaddItemsToBasketwill now return HTTP400for errors.- Error details for
addItemToBasketandaddItemsToBasketcan be found in theerrorproperty of the response. useBasketnow gracefully handles cases where adding items to the basket results in a smaller quantity being added than originally requested by checking againstAddToBasketFailureKind.
For reference, here's a comparison of the old and new implementation on how to utilize the newly returned HTTP code and `useBasket` quantity improvements for improved error handling:
Excerpt from utils/basket.ts
💥 Streamlining Data Fetching parameter: Transitioning to immediate and removing autoFetch
As of @scayle/[email protected] (released 7 June 2024), we've standardized on the immediate option to align with the parameters used in Nuxt 3's useAsyncData function. This change simplifies the data fetching process and ensures greater consistency across our composables.
What's changing?
- The
autoFetchoption withinuseRpchas been deprecated and replaced withimmediate. - This update affects all data fetching composables offered by
@scayle/storefront-nuxt.
Here's a quick example to guide you:
💥 Accessing User Access Tokens: Migrating to the getAccessToken RPC
We've updated how you can access a user's access token. Instead of directly accessing the storefrontAccessToken field on the UserAuthentication interface, you will now need to utilize the dedicated getAccessToken RPC. This change provides a more secure and streamlined approach to handling user tokens within the application.
What's changing?
- The
storefrontAccessTokenfield is removed from theUserAuthenticationinterface. - You must now use the
getAccessTokenRPC to retrieve the user's access token.
Here's a quick example to guide you:
💥 Standardized Error Handling in RPC Methods: BAPIError & BaseError Removed
This update streamlines error handling in RPC methods for better performance and alignment with web standards.
What's changing?
- We're removing the special handling for
BAPIErrorandBaseErrorin RPC methods. - These custom error classes are now treated like any other
Errorobject. - Uncaught
BAPIErrorandBaseErrorexceptions in RPC methods now result in a standard 500 status code response. BAPIErrorandBaseErrorhave been removed from@scayle/storefront-nuxt.- To specify a custom status code for an RPC method response, you should now return a
Responseobject directly.
How does this impact you?
For most users accustomed to our core RPC methods, this change won't require any action. However, if you've implemented custom RPC methods, we recommend reviewing and updating their error handling to utilize the standard Response object for custom status codes. This approach leverages the native capabilities of the Response object and provides consistency across your application.
To illustrate this change, consider the following example:
💥 Simplified API Access in Multi-Shop Setups: Global API Routing for Path-Based Shops
We've updated how API routes are managed for shops using path-based selection (path or path_except_default).
What's changing?
- API routes are now mounted globally: Instead of being nested under each shop's path, API routes will be available under a single, global path (default:
/api). - Shop-level overrides removed: The option to customize the
apiBasePathon a per-shop basis has been removed.
This change aims to simplify API route management and improve consistency across multi-shop environments.
If you are purging the cache via the API, make sure you use the correct endpoint and include the X-Shop-Id header.
💥Aligning with Nuxt useAsyncData: Replacing return values pending with status and fetch with refresh
We've given the useRpc composable an upgrade! It now offers a smoother and more powerful data fetching experience, aligning perfectly with the latest Nuxt 3 useAsyncData functionality.
What's changing?
- Unified Refresh: Say goodbye to the separate
fetchfunction! You can now refresh data using a single, intuitiverefreshfunction on theuseRpccomposable.1 - Enhanced Status Insights: The
pendingboolean flag is replaced by a more detailedstatusreturn value. This provides a clearer view of the fetching lifecycle with states like:idle,pending,success, anderror. - Modernized Interface: The overall interface now mirrors the streamlined approach of Nuxt 3's
useAsyncData, making for a more consistent and familiar developer experience.
This update not only impacts the useRpc composable directly, but also extends to other RPC composables relying on it, such as useProducts and useCategories.
Here's a quick example of how to move from refresh() to fetch():
Example from Storefront Application middleware/authGuard.global.ts:
Here's another quick example of how to move from pending / fetching to status:
Example from Storefront Boilerplate pages/c/[...categories]/[...slug]-[id].vue:
💥 Moving to Search v2: ReplacingsearchProducts and useSearch
As part of our transition to SCAYLE Search v2, we're relying on a new, streamlined search experience that prioritizes category pages. To simplify integration and ensure consistency, we're consolidating search functionality around the useStorefrontSearch composable from the storefront-nuxt package.
What's changing?
- Removal of
searchProductsRPC method: This method is no longer available. - Introduction of
getSearchSuggestions: Use this method for retrieving search suggestions provided by theuseStorefrontSearchcomposable from thestorefront-nuxtpackage. - Two Primary Suggestion Types:
- Product Suggestions: Triggered by entering a product ID (productId or referenceKey) in the search bar.
- Category Suggestions: Appear when typing category-like terms. These suggestions visually indicate applicable filters and redirect to filtered category pages upon selection.
- Replacement of
useSearchwithuseStorefrontSearch: We're transitioning to theuseStorefrontSearchcomposable for improved search functionality. - Introduction of the
useSearchDatacomposable: This composable simplifies search interactions by acting as a central proxy foruseStorefrontSearch. It manages the searchQuery and provides access to computed search data, debounced suggestions, and resolved routes, enhancing code reusability and maintainability. useStorefrontSearchcomposable consistency: The composableuseStorefrontSearchnow is consistent withuseRpcreturn values. Thefetchingboolean is replaced bystatuswith statesidle,pending,errorandsuccess.
For detailed implementation guidelines and further information, refer to Storefront Guide / Developer Guide / Features / Search.
Here's a quick example of how to migrate from the searchProducts to getSearchSuggestions RPC method:
Using searchProducts RPC directly:
const getSearchSuggestionsRpc = useRpcCall('searchProducts')
data.value = await searchProducts({
term: String(searchQuery.value),
...params,
})
Here's a quick example of how to migrate from useSearch to useStorefrontSearch:
Using useSearch (Search v1):
Here's a quick example of how to adapt the new return value changes of useStorefrontSearchfrom fetching to status:
💥 Renaming of useNavigationTree: Now useNavigationTreeById
We've updated the useNavigationTree composable for better clarity and functionality.
What's changing?
- The
useNavigationTreecomposable is now renamed touseNavigationTreeById. Its functionality, parameters and return values stay identical.
This change aims to improve the naming convention, making the purpose of the composable clearer.
💥 Removed Composable useFacet: Using dedicated Category-Specific Composables
The previous reliance on the useFacet composable for category-related product listings often led to unintended complexity. While aiming for versatility, its broad approach to faceting sometimes created excessive abstraction, making it difficult to grasp the underlying logic and implement customized solutions for specific category needs. Additionally, the internal state management within useFacet could sometimes lead to challenges with data consistency and multiple sources of truth, potentially introducing subtle bugs. To address these concerns, we moved to a more focused strategy using dedicated composables.
What's changing?
- Instead of using
useFacetfor category-related product listings, you'll now use the specialized composables:useProductsByCategoryto fetch products within a specific category.useFiltersoruseProductListFiltersto manage and apply filters to your product list.
How to migrate:
Transitioning to these purpose-built composables provides clearer separation of concerns and potentially improves performance. You can find detailed usage examples and API documentation under Storefront Guide / Developer Guide / Features / Product Listing Page.
Here's a rough example of how to use the dedicated composables:
We strongly encourage all projects to adopt these new composables as soon as possible. However, if immediate migration proves difficult, you can find the source code for the deprecated useFacet composable in the section below:
Please be aware that useFacet is no longer officially supported. Continuing to use it in your project is considered custom code and falls outside the scope of standard support.
Legacy Implementation: useFacet (Source Code)
💥 Removed Composable useQueryFilterState: Improving Filter Management with useFilter and useAppliedFilters
The previous useQueryFilterState composable, while functional, lacked the granularity needed for streamlined and transparent filter management within our storefront applications. To address this, we're introducing two new composables: useFilter and useAppliedFilters. This separation allows for a more organized and understandable approach to handling filters, making it easier to customize their behavior and integrate them seamlessly into your components.
What's changing?
- We're replacing the
useQueryFilterStatecomposable with two more specialized options:useFilter(see Reference Implementation below) provides a centralized way to fetch available filters, apply selections, reset filters, and manage filter-related logic. The exact application process depends on the type of filter being used.useAppliedFilters(from@scayle/storefront-product-listing) offers a direct way to access and work with the currently applied filters in the product listing context. It formats these active filters into aProductSearchQueryobject, crucial for fetching filtered results and understanding user selections. Additionally, it provides computed properties like the total count of applied filters.
Understanding the New Filter Behavior
The order of filters displayed in the Storefront Application is determined by a combination of the SCAYLE Panel configuration and the order of filters returned by the Storefront API. For more details check Storefront Guide / Developer Guide / Features / Pages / Product Listing Page.
Reference Implementation: useFilter.ts
Reference Implementation: useFilter.test.ts
We strongly encourage a swift transition to useFilter and useAppliedFilters for a more robust filter experience. However, if immediate migration presents difficulties, you can find the source code for the deprecated useQueryFilterState in the following section:
Please be aware that useQueryFilterState is no longer officially supported. Continuing its use in your project is considered custom code and falls outside the scope of standard support.
Legacy Implementation: useQueryFilterState (Source Code)
💥 Simplified IDP Integration: Using loginIDP for Callback Handling
We've optimized the Identity Provider (IDP) login process by replacing the handleIDPLoginCallback RPC method from the useIDP composable with the loginIDP function within the useAuthentication composable.
What's changing?
- The
handleIDPLoginCallbackRPC method is removed from theuseIDPcomposable. - Use the
loginIDPfunction from theuseAuthenticationcomposable for handling IDP login callbacks.
Here's a quick guide on updating your implementation:
💥 RpcContext Cleanup: Removing Legacy Attributes
We're cleaning up the RpcContext a bit to simplify its structure.
What's changing?
- The
isCmsPreviewattribute has been removed fromRpcContext. - The
storeCampaignKeywordattribute has been removed fromRpcContext.- The
campaignKeyis now automatically determined by fetching campaign data through the Storefront API client by retrieving a list of all campaigns from the API. It then narrows down the list by filtering for campaigns that are still active, ensuring any returned campaign is currently running. These active campaigns are then sorted by their start date, ensuring chronological order. Finally, it iterates through the sorted campaigns to find the first one that is currently active, returning its key as an identifier. If no active campaign is found or an error occurs, it returns nothing. - The
storeCampaignKeywordis also no longer used to determine the active campaign key. The functionality to run only certain campaigns for specific countries is now supported through the SCAYLE Panel out of the box.
- The
💥 Enhanced Key Security: Using SHA256 for Basket and Wishlist Key Generation
We've enhanced security for basket and wishlist keys by switching the default hashing algorithm from MD5 to the more robust SHA256.
What's changing?
- Basket and wishlist keys are now generated using
SHA256by default. - You can customize the hashing algorithm back to
MD5if needed through thestorefront.appKeys.hashAlgorithmproperty within yournuxt.config.tsfile.
💥 Simplified Composable Caching: enableDefaultGetCachedDataOverride Replaces disableDefaultGetCachedDataOverride
We've clarified the configuration option for controlling default caching behavior in composables and made a slight adjustment to its logic, especially regarding the handling of shared state with useRpc.
What's changing?
- Shared State: By default,
useRpcutilizes a shared cache. This means all instances called with the same key will share the same data. - Configuration Rename: The configuration option
disableDefaultGetCachedDataOverridehas been renamed toenableDefaultGetCachedDataOverride. - Reversed Logic:
- When
enableDefaultGetCachedDataOverrideisfalse(Default):- The default caching behavior of Nuxt is used. This differs from v7 which overrode this behavior by default.
- During SSR,
useAsyncDataprioritizes data fetching from Nuxt's internal data stores (nuxtApp.payload.datafor SSR based on the providedkey. - If no SSR data is found, it falls back to cached data from previous requests stored in
nuxtApp._asyncData[key]?.data.value.
- When
enableDefaultGetCachedDataOverrideistrue:- The default caching mechanism during SSR is overridden. This is identical to the default behavior in v7.
- Instead of using the built-in hydration data, a Storefront-specific
getCachedDatafunction will be used to fetch cached data:
- When
To maintain your currently used caching behavior:
Simply change the value of disableDefaultGetCachedDataOverride to its opposite in your nuxt.config.ts file (e.g., from false to true).
💥 Migrating User Authentication: Removing deprecated loginShopId Attribute
This update removes the reliance on the loginShopId attribute within the ShopUser interface for managing user shop associations. We're transitioning to a more streamlined approach using session cookies.
What's changing?
- The
loginShopIdattribute will be removed from theShopUserinterface. - User shop association will now be managed through session cookies.
Footnotes
- You can now refresh data using the
refreshfunction on theuseRpccomposable. ↩