Swapping Commerce Backends in Vue Storefront with a Middleware Layer
Learn how Vue Storefront’s middleware normalizes disparate commerce APIs so you can switch from Shopify to commercetools without rewriting frontend code, plus the trade‑offs to watch.
22 Oct 2025, 07:50 UTC

Problem: Vendor lock‑in forces frontend rewrites
When an e‑commerce project starts on Shopify, the frontend often ties directly to Shopify‑specific API shapes. Later, if the business decides to move to commercetools for better B2B features, the usual path is to rewrite product, cart, and checkout components to match the new backend’s data model. This rewrite is costly, error‑prone, and delays the migration.
Thesis: The middleware layer provides a stable contract
Vue Storefront places a Node.js/Express (or Fastify) middleware between the Nuxt 3 frontend and any commerce backend. Each backend ships as a dedicated module—for example, @vsf-enterprise/shopify-api or @vsf-enterprise/commercetools-api. These modules translate vendor‑specific responses into a shared GraphQL schema that defines normalized types for Product, Cart, User, and Checkout. Frontend composables such as useProduct() and useCart() call the middleware via server‑side fetch, so they never see the underlying backend’s shape.
How the unification works
- The middleware exposes a single GraphQL endpoint (
/graphql) that the frontend queries. - Each backend module implements resolvers that map its native fields to the shared schema (e.g., mapping Shopify’s
product.variantsto the unifiedproduct.variantslist). - Cross‑cutting concerns—auth token exchange, currency/locale normalization, tax delegation, and caching—are handled inside the middleware, keeping UI code agnostic.
Worked example: Swapping Shopify for commercetools
Below is a minimal, reproducible change that keeps all frontend components unchanged.
1. Inspect the current middleware registration
In a Vue Storefront project generated with npx @vue-storefront/cli create, the file middleware.config.js lists active API modules:
// middleware.config.js (before)
module.exports = {
integrations: {
shopify: {
location: '@vsf-enterprise/shopify-api',
configuration: {
// shopify credentials, API version, etc.
}
}
}
};
2. Replace the Shopify module with the commercetools module
Assuming you have installed @vsf-enterprise/commercetools-api via yarn or npm, edit the same file:
// middleware.config.js (after)
module.exports = {
integrations: {
commercetools: {
location: '@vsf-enterprise/commercetools-api',
configuration: {
// commercetools project key, client ID/secret, scopes, etc.
}
}
}
};
3. Verify that composables still work
The frontend code does not need to change. A typical product list component might look like:
// src/components/ProductList.vue
-
{{ p.name }} – {{ p.price }}
When the middleware runs with the commercetools module, the GraphQL query generated by useProduct() is sent to http://localhost:8080/graphql (or your deployed endpoint). The commercetools resolver transforms the commercetools product response into the unified shape, so p.name and p.price are present.
4. Check the normalized schema
Run the GraphQL codegen to ensure TypeScript stays in sync:
yarn graphql:codegen # updates src/types/graphql.d.ts
Open the generated types and confirm that fields like Product.name and Product.price exist regardless of which backend is active.
Trade‑offs and limitations
While the middleware reduces frontend churn, it introduces operational considerations:
- Schema extensions must be duplicated. If you add a custom field to the unified Product type for Shopify (e.g.,
customReleaseDate), you must also implement a resolver in the commercetools module that maps an equivalent commercetools attribute. Missing extensions cause runtime nulls. - Version pinning and contract testing. Middleware releases can change the shared GraphQL schema. Pin exact versions in
package.jsonand run contract tests (e.g., using graphql-backend-testing) in CI to catch breaking changes early. - Cold‑start latency in serverless. Deploying the middleware to Vercel, AWS Lambda, or Cloud Functions can add tens to hundreds of milliseconds on the first request after idle periods. Mitigate with provisioned concurrency, warm‑up cron jobs, or edge caching (e.g., Cloudflare Workers) in front of the GraphQL endpoint.
- Operational overhead. You now own a separate service that needs monitoring, logging, and alerting for each backend adapter. Treat the middleware as a critical path component.
Actionable closing
To safely adopt a backend swap:
- Create a feature branch and modify
middleware.config.jsas shown. - Run the middleware locally (
yarn dev:middleware) and execute a product search via GraphQL or the Nuxt dev server to confirm normalized fields appear. - Update your contract test suite to assert that the unified schema still contains the fields your UI consumes.
- Deploy to a staging environment, enable traffic splitting, and monitor TTFB and error rates for at least 30 minutes.
- If latency spikes appear, configure provisioned concurrency or edge caching before promoting to production.
By treating the middleware as the contract layer, you gain the ability to change commerce providers with minimal frontend impact—while staying vigilant about schema consistency, versioning, and serverless performance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.