> ## Documentation Index
> Fetch the complete documentation index at: https://swish.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Storefront API client

> Reference for the Swish SDK's Shopify Storefront API client - load products, images, recommendations, and run custom queries.

The `swish.storefront` namespace provides a client for the Shopify Storefront API. It handles authentication, localization (country/language from your storefront context), and response caching automatically.

```js theme={null}
const response = await swish.storefront.loadProduct({
  productHandle: "classic-leather-jacket",
});
```

All methods return a `StorefrontApiResponse`:

```ts theme={null}
interface StorefrontApiResponse<TData> {
  data: TData;
  errors: ResponseErrors | null;
}
```

## `loadProduct(args)`

Load product and variant data.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `productId` | `string` | No | Shopify product ID (numeric or GID). |
| `productHandle` | `string` | No | Product handle. |
| `variantId` | `string` | No | Load a specific variant. |
| `selectedOptions` | `Array<{ name, value }>` | No | Resolve a variant by selected options. |

Either `productId` or `productHandle` is required.

```js theme={null}
const response = await swish.storefront.loadProduct({
  productId: "8234567890123",
  variantId: "44234567890123",
});

const { product, variant } = response.data;
```

Returns `StorefrontApiResponse<{ product: Product | null; variant: ProductVariant | null }>`.

## `loadResolvedProduct(args)`

Load a single product in a render-ready shape. Unlike `loadProduct`, this transparently resolves [combined listings](https://swish.app/docs/guides/customize/transforms.md): it detects when a product belongs to a combined listing, loads the parent, and returns a product with the parent's options and availability matrix overlaid on the active child. This is the same resolution the built-in Swish UI uses, so a custom page renders variants identically.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `productId` | `string` | No | Shopify product ID (numeric or GID). |
| `productHandle` | `string` | No | Product handle. |
| `variantId` | `string` | No | Load a specific variant. |
| `selectedOptions` | `Array<{ name, value }>` | No | Resolve a variant by selected options. |

Either `productId` or `productHandle` is required.

```js theme={null}
const { product, variant } = await swish.storefront.loadResolvedProduct({
  productId: "8234567890123",
  variantId: "44234567890123",
});
```

Returns `Promise<{ product: Product | null; variant: ProductVariant | null }>`. Resolution failures past the initial fetch degrade gracefully (a warning is logged and the unresolved product is returned) rather than throwing.

### Build a custom wishlist page

To render a custom wishlist page, load the saved items (IDs only) and resolve each one with `loadResolvedProduct`. Products are cached individually, so repeat views and products shared across multiple lists are served from cache, and concurrent cache misses are automatically coalesced into batched Storefront requests.

Fire the calls concurrently with `Promise.all` rather than a sequential `for` loop: batching only kicks in when the `loadResolvedProduct` calls happen in the same tick, so a parallel map coalesces them into batched requests while a sequential loop pays one round-trip per item. You can pass an item straight through — it reads `productId`/`variantId` and ignores the rest. Keep each `item` next to its resolved product so you still have `item.id` (to unsave), `item.quantity`, and other fields when rendering.

```js theme={null}
// 1. Load the saved items (IDs only).
const { data: items } = await swish.api.items.list({ limit: 24 });

// 2. Enrich them with Shopify product data, keeping the item context.
//    Concurrent calls are cached per product and batched automatically.
const entries = await Promise.all(
  items.map(async (item) => ({
    item,
    ...(await swish.storefront.loadResolvedProduct(item)),
  })),
);

// 3. Render.
for (const { item, product, variant } of entries) {
  console.log(item, product, variant);
}
```

> [!TIP]
>
> For thumbnail-only grids where you don't need full product data, [`loadProductImages`](#loadproductimagesargs) is a lighter call.

## `loadProductImages(args)`

Load images for multiple products at once. Useful for rendering product grids without fetching full product data.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `items` | `Array<{ productId, variantId? }>` | Yes | Products to load images for. IDs are numeric. |

```js theme={null}
const response = await swish.storefront.loadProductImages({
  items: [
    { productId: 8234567890123 },
    { productId: 8234567890456, variantId: 44234567890789 },
  ],
});

// response.data: Array<{ id, altText, url }>
```

Returns `StorefrontApiResponse<Array<{ id: string; altText: string; url: string }>>`. On error, `data` is `null` and `errors` contains the error.

## `loadProductRecommendations(args)`

Load product recommendations from Shopify.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `productId` | `string` | No | Shopify product GID. |
| `productHandle` | `string` | No | Product handle. |
| `intent` | `ProductRecommendationIntent` | No | Recommendation intent (e.g. `"RELATED"`, `"COMPLEMENTARY"`). |

Either `productId` or `productHandle` is required.

```js theme={null}
const response = await swish.storefront.loadProductRecommendations({
  productHandle: "classic-leather-jacket",
  intent: "RELATED",
});
```

Returns `StorefrontApiResponse<{ productRecommendations?: Product[] | null }>`.

## `loadProductId(args)`

Resolve a product ID from a handle. Used internally by the SDK when only a handle is available (e.g. from a URL).

| Parameter | Type | Required | Description |
| - | - | - | - |
| `productHandle` | `string` | Yes | Product handle. |

```js theme={null}
const response = await swish.storefront.loadProductId({
  productHandle: "classic-leather-jacket",
});
```

## `loadProductHandle(args)`

Resolve a product handle from an ID. The inverse of `loadProductId` - useful when you have a saved item (which stores IDs) and need to build a link to the product page.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `productId` | `string \| number` | Yes | Shopify product ID (numeric or GID). |

```js theme={null}
const response = await swish.storefront.loadProductHandle({
  productId: "8234567890123",
});
```

Throws when `productId` is missing.

## `loadCollectionProducts(args)`

Load a page of products from a Shopify collection. Country and language are injected from your storefront context automatically.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `handle` | `string` | Yes | Collection handle. |
| `first` | `number` | Yes | Number of products to return. Shopify caps this at 250. |
| `after` | `string \| null` | No | Pagination cursor from a previous call's `pageInfo.endCursor`. |

```js theme={null}
const response = await swish.storefront.loadCollectionProducts({
  handle: "best-sellers",
  first: 12,
});

// response.data.collection: { title, handle, onlineStoreUrl, products }
// products.nodes carries `id` only; products.pageInfo has `hasNextPage` and `endCursor`.
```

Returns the collection's `title`, `handle`, and `onlineStoreUrl` alongside the page of products. Each product node carries only its `id` - pass those to `loadProduct` or `loadResolvedProduct` when you need full product data. Throws when `handle` is missing.

## `query(query, variables)`

Run a custom Storefront API GraphQL query. Country and language from the storefront context are included automatically via the `@inContext` directive.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `string` | Yes | GraphQL query string. |
| `variables` | `Record<string, unknown>` | Yes | Query variables. |

```js theme={null}
const response = await swish.storefront.query(
  `query GetCollection($handle: String!) @inContext(country: $country, language: $language) {
    collection(handle: $handle) {
      title
      products(first: 10) {
        nodes { id title }
      }
    }
  }`,
  { handle: "summer-collection" },
);
```

Returns `StorefrontApiResponse<TData>`.

## `fetch(input, init?)`

Low-level fetch with caching. Sends a request through the Storefront API client's cache layer.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `input` | `string \| URL \| Request` | Yes | The resource to fetch. |
| `init` | `RequestInit` | No | Fetch options. |

Returns `Promise<Response>`.

## `clearCache()`

Clear the short-lived Storefront API response cache.

```js theme={null}
await swish.storefront.clearCache();
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.