Wix-Rest-Api-Migration
Design Log 22: Wix REST API Migration
Written for AI agents. See Log Methodology Note below for details.
Status
Draft
Background
The Jay Framework Wix packages (wix-stores, wix-cart, wix-server-client) use the Wix SDK's module system (client.use(productsV3)) to call Wix APIs. This pulls in @wix/stores, @wix/ecom, @wix/categories, @wix/data-extension-schema — totaling 53 MB in node_modules. BaaS has a 20 MB deployment limit, making the current approach unworkable.
The Wix SDK does two things:
- Auth management —
ApiKeyStrategy,OAuthStrategy, token lifecycle, OAuth flows (login, register, token refresh, PKCE). This is complex and valuable. - API modules —
productsV3,currentCart,categories— typed wrappers around REST endpoints. These add bulk but are functionally justfetch()calls with auth headers.
Problem
Replace the SDK API modules with direct REST calls while keeping the SDK for auth. This eliminates 53 MB of @wix/* module dependencies, making BaaS deployment viable (~3.5 MB instead of 55+ MB).
Design
Keep @wix/sdk for Auth
The SDK's auth system stays unchanged:
// Server — API key auth
const client = createClient({
auth: ApiKeyStrategy({ apiKey, siteId }),
modules: {}, // no modules needed
});
// Client — OAuth auth
const client = createClient({
auth: OAuthStrategy({ clientId, tokens }),
modules: {},
});
The client.auth provides:
getAuthHeaders()→{ headers: { Authorization: '...' } }generateVisitorTokens(),renewToken(),setTokens(),getTokens()login(),register(),processVerification(),loggedIn(),logout()getMemberTokensForDirectLogin(),sendPasswordResetEmail()
All of this stays as-is.
REST API Wrapper Pattern
Create a typed REST client that uses the SDK's auth headers:
import type { WixClient } from '@wix/sdk';
const WIX_API_BASE = 'https://www.wixapis.com';
async function wixFetch<T>(
client: WixClient,
method: string,
path: string,
body?: any,
): Promise<T> {
const { headers } = await client.auth.getAuthHeaders();
const response = await fetch(`${WIX_API_BASE}${path}`, {
method,
headers: {
...headers,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
throw new Error(`Wix API ${response.status}: ${path}`);
}
return response.json();
}
Where REST Functions Live
Option A: In a shared wix-rest utility module
packages/wix-utils/lib/
├── media.ts # existing media utilities
└── rest/
├── client.ts # wixFetch helper
├── stores.ts # queryProducts, getProductBySlug, ...
├── cart.ts # getCurrentCart, addToCart, ...
└── categories.ts # queryCategories
Pros: Centralized, reusable, one place to maintain REST endpoints. Cons: Another package dependency, all API functions in one place.
Option B: In each package's service/context
packages/wix-stores/lib/
├── services/
│ └── wix-stores-service.ts # uses wixFetch for server calls
├── contexts/
│ └── wix-stores-context.ts # uses wixFetch for client calls
└── rest/
└── stores-api.ts # queryProducts, getProductBySlug, ...
packages/wix-cart/lib/
├── services/
│ └── wix-cart-service.ts
├── contexts/
│ └── wix-cart-context.ts
└── rest/
└── cart-api.ts # getCurrentCart, addToCart, ...
Pros: Each package is self-contained, REST functions co-located with usage.
Cons: wixFetch helper duplicated or shared via wix-utils.
Option C: REST functions in the service/context directly (no separate layer)
The REST calls are inlined where they're currently used — inside the service (server) and context (client):
// In wix-stores-service.ts (server)
export function provideWixStoresService(wixClient: WixClient) {
async function queryProducts(query) {
const { headers } = await wixClient.auth.getAuthHeaders();
const res = await fetch('https://www.wixapis.com/stores/v3/products/query', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
});
return res.json();
}
// ...
}
Pros: Simplest, no abstraction, easy to understand. Cons: Auth header logic repeated in every call, harder to maintain endpoints.
Questions & Answers
Q1: Which option for the REST function organization?
A: Per package, each file one API call. Folder named wix-apis/ (not rest/ — we're implementing API clients, not exposing REST APIs).
packages/wix-stores/lib/
├── wix-apis/
│ ├── query-products.ts
│ ├── get-product.ts
│ ├── query-categories.ts
│ ├── query-inventory.ts
│ ├── list-customizations.ts
│ └── types.ts # copied/adjusted types from SDK
├── services/
├── contexts/
└── components/
Q2: Should the wixFetch helper be in wix-utils or wix-server-client?
A: In wix-server-client — it owns the Wix client and auth. The helper uses client.auth.getAuthHeaders() which comes from the SDK client that wix-server-client manages.
Q3: The current code uses typed response objects from @wix/auto_sdk_stores_products-v-3 etc. for type safety. Without those, do we define our own types or use any?
A: Copy and adjust the types from the SDK. Place them in the wix-apis/ directory alongside the API functions.
Q4: The client-side wix-cart-context.ts uses wixClient.use(currentCart) methods directly. Should the REST wrapper use the same auth mechanism (client.auth.getAuthHeaders()) on both client and server?
A: Yes. The wixFetch helper should work with any WixClient — server (ApiKeyStrategy) or client (OAuthStrategy). Both provide getAuthHeaders().
Q5: Some API calls use query builder patterns (client.use(categories).queryCategories().eq('visible', true).limit(100).find()). The REST equivalent is a JSON query body. Is the translation straightforward?
A: Drop query builders. Use plain JSON query bodies directly.
API Inventory
Stores (wix-stores package)
| Current SDK call | REST endpoint | Method | Used in |
|---|---|---|---|
productsV3.queryProducts(query) |
/stores/v3/products/query |
POST | stores-actions.ts (searchProducts, getProductBySlug) |
productsV3.getProduct(id) |
/stores/v3/products/{id} |
GET | stores-actions.ts (related products) |
categories.queryCategories() |
/stores/v1/categories/query |
POST | stores-actions.ts (getCategories) |
inventoryItemsV3.queryInventory() |
/stores/v3/inventoryItems/query |
POST | stores-actions.ts (getVariantStock) |
customizationsV3.listCustomizations() |
/stores/v3/customizations |
GET | product-page.ts (modifiers) |
dataExtensionSchemas.querySchemas() |
/data-extension-schema/v1/schemas/query |
POST | wix-stores-service.ts (setup) |
Cart (wix-cart package)
| Current SDK call | REST endpoint | Method | Used in |
|---|---|---|---|
currentCart.getCurrentCart() |
/ecom/v1/carts/current |
GET | wix-cart-context.ts |
currentCart.addToCurrentCart(items) |
/ecom/v1/carts/current/add |
POST | wix-cart-context.ts |
currentCart.removeLineItemsFromCurrentCart(ids) |
/ecom/v1/carts/current/removeLineItems |
POST | wix-cart-context.ts |
currentCart.updateCurrentCartLineItemQuantity(items) |
/ecom/v1/carts/current/updateLineItemQuantity |
POST | wix-cart-context.ts |
currentCart.updateCurrentCart(cart) |
/ecom/v1/carts/current |
PATCH | wix-cart-context.ts (coupon) |
currentCart.removeCouponFromCurrentCart() |
/ecom/v1/carts/current/removeCoupon |
POST | wix-cart-context.ts |
currentCart.estimateCurrentCartTotals() |
/ecom/v1/carts/current/estimateTotals |
POST | wix-cart-context.ts |
Server Client (wix-server-client package)
| Current SDK call | REST equivalent | Notes |
|---|---|---|
createClient({ auth: ApiKeyStrategy(...) }) |
Keep as-is | Auth only, no modules |
createClient({ auth: OAuthStrategy(...) }) |
Keep as-is | Auth only, no modules |
client.auth.* methods |
Keep as-is | OAuth flows stay in SDK |
Impact
What's removed
@wix/stores(includes@wix/auto_sdk_stores_products-v-3,@wix/auto_sdk_stores_inventory-items-v-3,@wix/auto_sdk_stores_customizations-v-3)@wix/ecom(includes@wix/auto_sdk_ecom_current-cart,@wix/auto_sdk_ecom_cart-v-2,@wix/auto_sdk_ecom_checkout,@wix/auto_sdk_ecom_orders,@wix/auto_sdk_ecom_draft-orders, ...)@wix/categories(includes@wix/auto_sdk_categories_categories)@wix/data-extension-schema- All their transitive dependencies
What's kept
@wix/sdk— auth strategies, token management, OAuth flows@wix/sdk-types— shared types (if needed)
Size impact
- Before: 53 MB
@wix/*in node_modules - After: ~200 KB (
@wix/sdkonly, already bundled in entry.mjs) - BaaS dist: drops from 55+ MB to ~3.5 MB
Risk
- REST endpoint URLs could change (unlikely for v3 APIs)
- Response shapes might differ slightly from SDK types (need to verify)
- Query builder patterns need manual JSON translation
Implementation Plan
Phase 0: Exploration — validate REST approach
- Create
exploration/wix-rest-api/ - Use
@wix/sdkwithApiKeyStrategyfor server auth,OAuthStrategyfor client auth - Call
client.auth.getAuthHeaders()and use withfetch()directly - Test 1:
POST /stores/v3/products/query— query products, verify response shape matches SDK - Test 2:
GET /ecom/v1/carts/current— get current cart via OAuth, verify client auth works - Validate: response types, error handling, auth headers, both environments
Phase 1: wixFetch helper in wix-server-client
- Add
wixFetch<T>(client, method, path, body?)towix-server-client - Uses
client.auth.getAuthHeaders()— works with both ApiKey and OAuth - Export from the package for use by other packages
Phase 2: Stores API functions
- Create
wix-stores/lib/wix-apis/with one file per API call - Copy and adjust response types from SDK modules
- Replace usage in services, components, and actions
- Remove
@wix/stores,@wix/categories,@wix/data-extension-schemafrom dependencies - Remove from vite.config.ts externals
Phase 3: Cart API functions
- Create
wix-cart/lib/wix-apis/with one file per API call - Copy and adjust response types from SDK modules
- Replace usage in service and context (both server and client)
- Remove
@wix/ecomfrom dependencies - Remove from vite.config.ts externals
Phase 4: Verify locally
- Rebuild all packages
- Run store-light example with
jay-stack dev - Verify: product pages render, search works, cart add/remove/checkout works
Phase 5: BaaS deployment
- Rebuild for BaaS:
build:production→deploy:build-entry→deploy:deploy - dist/node_modules should now be ~1 MB (only
@jay-framework/*) - Verify deployed site works end-to-end
Trade-offs
| Decision | Benefit | Cost |
|---|---|---|
| Keep @wix/sdk for auth | OAuth complexity handled, tested, maintained by Wix | Still a dependency (~200 KB bundled) |
| Direct REST calls | 53 MB → 0 MB for API modules, BaaS-deployable | Must maintain endpoint URLs, lose query builder |
| Types from response | Lightweight, no extra deps | Less type safety than SDK-generated types |
| wixFetch in wix-server-client | Shared across packages, single auth pattern | One more export from wix-server-client |
Implementation Results
Completed (2026-05-31)
Phase 0: Exploration — Validated REST approach in exploration/wix-rest-api/. Both server (ApiKey) and client (OAuth) auth work with wixFetch. Captured real API responses as reference files.
Phase 1: wixFetch helper — Added to wix-server-client/lib/wix-fetch.ts. Exports wixFetch, WixApiError, WixFilter, WixSort, WixPaging, WixCursorPaging. Shared types for filters, sorting, and paging.
Phase 2: Stores API functions — Created wix-stores/lib/wix-apis/ with 9 functions:
queryProducts,searchProducts,getProduct,getProductBySlugqueryCategories,getCategoryqueryInventory,queryCustomizations,querySchemas
Updated all consumers: stores-actions.ts, product-page.ts, product-search.ts, category-list.ts, related-products.ts, setup.ts, wix-stores-context.ts, wix-stores-service.ts.
Phase 3: Cart API functions — Created wix-cart/lib/wix-apis/ with 7 functions:
getCurrentCart,addToCurrentCart,removeLineItemsFromCurrentCartupdateCurrentCartLineItemQuantity,updateCurrentCartremoveCouponFromCurrentCart,estimateCurrentCartTotals
Updated wix-cart-context.ts, cart-helpers.ts, wix-cart-service.ts, wix-cart-service-marker.ts.
Phase 4: Verification — store-light dev server works. Products page, product detail pages, and homepage all return 200.
Dependencies removed
@wix/stores(was ~6 MB with transitive deps)@wix/ecom(was ~40 MB with transitive deps)@wix/categories(was ~2 MB)@wix/data-extension-schema(was ~1.5 MB)@wix/sdk-runtime@wix/sdk-types
Dependencies kept
@wix/sdk— auth strategies, token management, OAuth flows
Key findings
- Search API uses GET with base64 query params on the edge, but POST works on
www.wixapis.com - Aggregation format:
fieldPathis a sibling oftype/scalar/rangein each aggregation item, not nested inside them - Response shape differences: REST uses
idnot_id,media.main.image.urlnotmedia.main.url. Product mapper needsas anycasts until types are fully aligned. - Query builder replacement: SDK's
.eq('visible', true).limit(100).find()becomes{ filter: { visible: true }, paging: { limit: 100 } }— straightforward
Known issues
product-page.tsstill imports types from@wix/auto_sdk_stores_products-v-3(type-only, erased at runtime). Should be replaced with local types eventually.- Product mapper uses
as anycasts for REST response products due to media shape differences between SDK and REST. Needs type alignment. - Cart
EstimateTotalsResultusesanyfor the response type. Should be properly typed.
Phase 5: BaaS deployment — pending
With SDK modules removed, dist/node_modules/ should drop from 53+ MB to ~1 MB (only @jay-framework/* packages). Needs verification.
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.