Skip to main content

๐Ÿง  Shopify Client SDK

Welcome to the heart of @nextshopkit/sdk and @nextshopkit/pro.
Everything starts with createShopifyClient() โ€” a typed, server-safe way to access Shopify's Storefront API.


๐Ÿ›  What is the client?โ€‹

The client is your gateway to Shopify. It wraps all of your requests and gives you access to prebuilt methods like:

  • getProduct() โ€” Fetch a single product by handle
  • getCollection() โ€” Fetch multiple products with filters, sorting, and pagination within a collection
  • getSearchResult() โ€” Search for products, collections, or other resources (CORE/PRO)
  • getProductVariant() โ€” Fetch a single product variant by ID (PRO only)
  • getProductVariants() โ€” Fetch multiple product variants by IDs (PRO only)
  • getPolicy() โ€” Fetch a single shop policy by type (PRO only)
  • getPolicies() โ€” Fetch all shop policies (PRO only)
  • Cart functions โ€” Complete cart management with programmatic API

โš™๏ธ Initializing the clientโ€‹

You only need to do this once โ€” usually inside:

lib/NextShopKit/client.ts
Caching & Performance Notes

When initializing the client, you can optionally enable caching to improve performance:

  • enableMemoryCache: stores Shopify responses in memory for quicker reuse
  • enableVercelCache: integrates with Vercel ISR caching (use with revalidate)
  • defaultCacheTtl: memory cache duration in seconds
  • defaultRevalidate: Vercel revalidate time in seconds

These options reduce redundant API calls and improve response times.

import {
createShopifyClient,
GetProductOptions,
FetchProductResult,
GetCollectionOptions,
FetchCollectionResult,
GetSearchResultOptions,
FetchSearchResult,
GetPoliciesOptions,
FetchPoliciesResult,
GetPolicyOptions,
FetchPolicyResult,
GetProductVariantOptions,
FetchProductVariantResult,
GetProductVariantsOptions,
FetchProductVariantsResult,
} from "@nextshopkit/sdk"; // or "@nextshopkit/pro"

// Cache options can be used both for CORE and PRO tier.
const client = createShopifyClient({
shop: process.env.SHOPIFY_STORE_DOMAIN!,
token: process.env.SHOPIFY_ACCESS_TOKEN!,
apiVersion: "2025-04",
enableMemoryCache: true,
defaultCacheTtl: 300,
enableVercelCache: true,
defaultRevalidate: 60,
});

// The cache option specified here overrides the default
// client-level cache settings and is exclusively available
// in the PRO tier. Note that the CORE tier does not
// support function-specific cache configurations.
export const getProduct = async (
args: GetProductOptions
): Promise<FetchProductResult> =>
client.getProduct(args, {
cacheTtl: 60,
revalidate: 60,
useMemoryCache: true,
useVercelCache: true,
});

export const getCollection = async (
args: GetCollectionOptions
): Promise<FetchCollectionResult> => client.getCollection(args);

export const getSearchResult = async (
args: GetSearchResultOptions
): Promise<FetchSearchResult> => client.getSearchResult(args);

export const getPolicies = async (
args?: GetPoliciesOptions
): Promise<FetchPoliciesResult> => client.getPolicies(args);

// PRO-only functions
export const getPolicy = async (
args: GetPolicyOptions
): Promise<FetchPolicyResult> => client.getPolicy(args);

export const getProductVariant = async (
args: GetProductVariantOptions
): Promise<FetchProductVariantResult> => client.getProductVariant(args);

export const getProductVariants = async (
args: GetProductVariantsOptions
): Promise<FetchProductVariantsResult> => client.getProductVariants(args);

export default client;

๐Ÿ” Environment Variablesโ€‹

Your .env.local file should contain appropriate variables for your use case:

For server-side operations (recommended for data fetching):

SHOPIFY_ACCESS_TOKEN=your-access-token
SHOPIFY_STORE_DOMAIN=your-store.myshopify.com

For client-side operations (required for cart functionality):

NEXT_PUBLIC_SHOPIFY_ACCESS_TOKEN=your-storefront-access-token
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=your-store.myshopify.com

Server-side variables are never exposed to the client. Client-side variables with NEXT_PUBLIC_ prefix are accessible in the browser but should only use Storefront API tokens with minimal permissions.


๐Ÿงฉ What does the client return?โ€‹

Every SDK method returns the same structure:

const { data, error } = await getProduct({ handle: "solar-kit-9kw" });

if (error || !data) {
// Handle the error gracefully
}

You always get:

  • data โ€” The result (typed based on the method)
  • error โ€” A string if something went wrong, or null if successful

๐Ÿ“Š Available Methodsโ€‹

MethodDescriptionTierDocumentation
getProduct()Fetch a single product by handle or IDCORE/PROSee docs โ†’
getCollection()Fetch a collection with products & filtersCORE/PROSee docs โ†’
getSearchResult()Search products, collections, articlesCORE/PROSee docs โ†’
getProductVariant()Fetch single variant with product contextPROSee docs โ†’
getProductVariants()Fetch multiple variants with contextPROSee docs โ†’
getPolicy()Fetch a single shop policy by typePROSee docs โ†’
getPolicies()Fetch all shop policiesPROSee docs โ†’
Cart FunctionsComplete cart management APICORE/PROSee docs โ†’

๐Ÿ”’ Security & Usage Guidelinesโ€‹

Use private environment variables and server-side execution for:

  • Product pages and collections
  • Search results and static generation
  • API routes and server actions
  • Better performance and SEO
// Server Component or API route
const { data, error } = await getProduct({
handle: "product-handle",
});

โš›๏ธ Client-side Usage (Required for interactive features)โ€‹

Use NEXT_PUBLIC_ environment variables for:

  • Cart management (CartProvider, useCart())
  • Real-time inventory updates
  • Interactive product configurators
  • Dynamic user-specific content
// Client component with cart functionality
import { useCart } from "@nextshopkit/sdk/client";

function CartButton() {
const { addProducts, cart } = useCart();
// Cart operations work client-side
}

๐Ÿšจ Security Best Practicesโ€‹

  • Server-side: Use private tokens, keep credentials secure
  • Client-side: Only use Storefront API tokens (never Admin API)
  • Permissions: Ensure minimal required permissions for client-side tokens
  • Validation: Consider rate limiting and request validation

๐Ÿ›’ Cart Functionsโ€‹

The client also provides programmatic cart functions for SSR, API routes, or custom logic:

// Cart management
const cart = await client.createCart();
const cart = await client.getCart(cartId);
await client.addToCart(cartId, lines);
await client.removeFromCart(cartId, lineId);
await client.updateCartItem(cartId, lineId, quantity);
await client.emptyCart(cartId);
await client.applyDiscount(cartId, code);
await client.removeDiscount(cartId);
await client.updateCartAttributes(cartId, attributes);
await client.updateBuyerIdentity(cartId, buyerIdentity);
await client.mergeCarts(sourceCartId, destinationCartId);

๐Ÿ“š Method Overviewโ€‹

MethodDescriptionTierLink
getProduct()Fetch a product by handleCORE/PROSee docs โ†’
getCollection()Fetch multiple products with filters & sortingCORE/PROSee docs โ†’
getSearchResult()Search for products, collections, or resourcesCORE/PROSee docs โ†’
getProductVariant()Fetch a single product variant by IDPROSee docs โ†’
getProductVariants()Fetch multiple product variants by IDsPROSee docs โ†’
getPolicy()Fetch a single shop policy by typePROSee docs โ†’
getPolicies()Fetch all shop policiesPROSee docs โ†’
Cart FunctionsComplete cart management APICORE/PROSee docs โ†’

โœ… Best practicesโ€‹

  • Place the client setup inside lib/NextShopKit/client.ts
  • Create wrappers like getProduct, getCollection, and getPolicies so you can mock or extend them later
  • Handle error and null data gracefully
  • Group usage per domain (lib/NextShopKit/products.ts, cart.ts, etc.)

๐Ÿงช Need examples?โ€‹