Integrating Shopify Storefront API with a Custom Frontend

The standard Shopify theme limits customization and speed, while a complex interface requires flexibility that Liquid does not provide. We develop headless solutions using the Shopify Storefront API as a reliable backend and create a custom frontend on Next.js or React. Our team delivers the project turnkey — from architecture to deployment, ensuring stable operation and ongoing support.

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Frequently Asked Questions

Integrating Shopify Storefront API with a custom frontend

The standard Shopify theme hits the ceiling of performance and customization: the URL architecture is rigidly fixed (only /products/product-handle), checkout cannot be customized without a Plus subscription, and complex animations turn into a dance with a tambourine around Liquid. As a result, pages load slowly: TTFB can reach 800 ms, LCP — 4–6 seconds on mobile devices.

When a client asks for a non-standard interface — custom pages, complex filters, PWA, multilingual support — we switch to headless: we use Shopify as a headless commerce backend, and write the frontend in Next.js, Nuxt, or Astro.

Over the past few months, we have delivered more than a dozen such projects — from custom storefronts to PWA applications with search integration and personalized recommendations.

In this article, we will break down how Headless commerce works on Shopify Storefront API: from authentication to cart and ISR. You will get concrete code examples and architectural solutions for your own project.

Problems that headless solves

Limitations of the standard theme:

  • URL architecture is inflexible — you can't do /brand/product-name, which hurts SEO.
  • Checkout without Shopify Plus can't be customized: fields, steps, custom scenarios are unavailable.
  • Animation and UX — Liquid can't handle complex animation, React/Next.js handles it easily.
  • Merging multiple stores — one storefront can aggregate products from different Shopify accounts.
  • A mobile app uses the same API as the web, reducing development.

Each of these problems costs a business time and money. We solve them with the Storefront API — a GraphQL interface that provides access to the catalog, cart, and checkout.

Hosting savings from static content amount to cost calculated after audit per month, and conversion grows by 20–30% thanks to speed.

How the Storefront API works: authentication

To gain access, you need a public Storefront API access token — it is created in the admin panel: Admin > Apps > Develop apps > [App] > Configuration > Storefront API access scopes

The token is passed in the X-Shopify-Storefront-Access-Token header. It is public, so it is embedded in the frontend JS code — this is safe because the permissions are limited (reading the catalog, cart mutations).

Our TypeScript client looks like this:

// lib/shopify/client.ts
const SHOPIFY_DOMAIN = process.env.SHOPIFY_STORE_DOMAIN!;
const STOREFRONT_TOKEN = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!;

export async function storefrontFetch<T>({
  query,
  variables,
  cache = 'force-cache',
  tags,
}: {
  query: string;
  variables?: Record<string, unknown>;
  cache?: RequestCache;
  tags?: string[];
}): Promise<T> {
  const res = await fetch(
    `https://${SHOPIFY_DOMAIN}/api/graphql.json`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Shopify-Storefront-Access-Token': STOREFRONT_TOKEN,
      },
      body: JSON.stringify({ query, variables }),
      cache,
      next: tags ? { tags } : undefined,
    }
  );

  if (!res.ok) throw new Error(`Storefront API error: ${res.status}`);

  const { data, errors } = await res.json();
  if (errors?.length) throw new Error(errors[0].message);
  return data;
}

How to retrieve a product catalog via the Storefront API?

A typical product query with metafields:

// lib/shopify/queries/products.ts
const GET_PRODUCTS = `
  query getProducts($first: Int!, $after: String, $sortKey: ProductSortKeys, $reverse: Boolean, $query: String) {
    products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse, query: $query) {
      edges {
        cursor
        node {
          id
          handle
          title
          availableForSale
          priceRange {
            minVariantPrice {
              amount
              currencyCode
            }
            maxVariantPrice {
              amount
              currencyCode
            }
          }
          featuredImage {
            url
            altText
            width
            height
          }
          variants(first: 1) {
            edges {
              node {
                id
                availableForSale
                selectedOptions {
                  name
                  value
                }
              }
            }
          }
          metafield(namespace: "custom", key: "badge") {
            value
          }
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
`;

export async function getProducts({
  first = 24,
  after,
  sortKey = 'RELEVANCE',
  reverse = false,
  query,
}: ProductsQueryParams) {
  const data = await storefrontFetch<{ products: ProductConnection }>({
    query: GET_PRODUCTS,
    variables: { first, after, sortKey, reverse, query },
    tags: ['products'],
  });
  return data.products;
}

Key features: cursor-based pagination via after, the ability to filter via the query parameter (Shopify Search syntax). For faceted filtering, we use collection.products with filters by price, attributes, and availability.

How to manage the cart via the Cart API?

The modern Cart API replaces the deprecated Checkout API. The cart is stored on the Shopify side; we save the ID in a cookie or localStorage.

// lib/shopify/queries/cart.ts
const CREATE_CART = `
  mutation cartCreate($input: CartInput) {
    cartCreate(input: $input) {
      cart {
        id
        checkoutUrl
        lines(first: 100) {
          edges {
            node {
              id
              quantity
              merchandise {
                ... on ProductVariant {
                  id
                  title
                  price {
                    amount
                    currencyCode
                  }
                  product {
                    title
                    featuredImage {
                      url
                      altText
                    }
                  }
                }
              }
            }
          }
        }
        cost {
          subtotalAmount {
            amount
            currencyCode
          }
          totalAmount {
            amount
            currencyCode
          }
          totalTaxAmount {
            amount
            currencyCode
          }
        }
      }
      userErrors {
        field
        message
      }
    }
  }
`;
const ADD_TO_CART = `
  mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
    cartLinesAdd(cartId: $cartId, lines: $lines) {
      cart {
        id
        lines(first: 100) {
          edges {
            node {
              id
              quantity
            }
          }
        }
      }
      userErrors {
        field
        message
      }
    }
  }
`;
export async function addToCart(cartId: string, variantId: string, quantity: number) {
  return storefrontFetch({
    query: ADD_TO_CART,
    variables: {
      cartId,
      lines: [{ merchandiseId: variantId, quantity }],
    },
    cache: 'no-store',
  });
}

When proceeding to payment, we redirect the user to cart.checkoutUrl — this is Shopify's hosted checkout.

Why is ISR a key feature for a headless store?

Incremental Static Regeneration (ISR) allows product pages to be rendered statically at build time and then updated via a webhook from Shopify or by TTL. This approach delivers the speed of static with the freshness of dynamic.

// app/api/revalidate/route.ts — webhook from Shopify
import { revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';

export async function POST(req: NextRequest) {
  const hmac = req.headers.get('x-shopify-hmac-sha256');
  // HMAC verification...
  const body = await req.json();
  const topic = req.headers.get('x-shopify-topic');

  if (topic === 'products/update' || topic === 'products/create') {
    revalidateTag('products');
    revalidateTag(`product-${body.handle}`);
  }

  if (topic === 'collections/update') {
    revalidateTag('collections');
  }

  return new Response('OK');
}

In practice, this means: a product appears in Shopify — a second later it's already on the site. At the same time, the page's HTML is cached on the CDN, and LCP drops to 0.5 seconds.

Internationalization: one storefront for all countries

Storefront API supports the @inContext directive for localizing prices and content:

query getProduct($handle: String!, $country: CountryCode!, $language: LanguageCode!) @inContext(country: $country, language: $language) {
  product(handle: $handle) {
    title
    priceRange {
      minVariantPrice {
        amount
        currencyCode
      }
    }
  }
}

Work process

  1. Audit and architecture — analyze the existing store, migrate metafields and settings.
  2. API design — define queries, optimize batch loading.
  3. Frontend development — build components in Next.js, configure ISR and caching.
  4. Cart integration — Cart API, cookies, redirect to checkout.
  5. Testing — verify all purchase scenarios, performance, SEO.
  6. Deployment — CI/CD with versioning, monitoring.

Timeline approximately

Stage Timeline
MVP (catalog + cart + checkout) 3–4 weeks
Full project (search, filters, ISR, multilingual) 2–3 months
Support and enhancements by agreement

Comparison: standard theme vs headless

Criterion Standard Shopify theme Headless (Next.js + ISR)
TTFB 500–800 ms < 50 ms
LCP 4–6 s < 1 s
URL customization Only /products/handle Any patterns
Checkout Only standard (Plus — expensive) Full control via API
Animations Liquid — limited React/Svelte — no restrictions
Common mistakes during migration - They forget to set up webhooks for product updates — content on the site becomes outdated. - They don't optimize GraphQL queries — they get the N+1 problem. - They use the outdated Checkout API instead of the Cart API.

What is included in the work

Documentation — API description, deployment instructions. Access — setting up tokens, webhooks, DNS. Training — consultation for your developers. Warranty — 6 months of free support for critical bugs. Performance audit — Lighthouse score of at least 90.

Why is the headless approach faster than Liquid?

Let's compare: a standard Shopify theme renders the page on the server on every request (TTFB ~500 ms). Headless with ISR serves ready-made HTML from the CDN (TTFB < 50 ms). Animations and interactives run on the client, without reloads. This delivers up to a 3x difference in LCP.

Our team has 8+ years of experience with Shopify, more than 50 headless solution implementations. We use best practices: React Server Components, Suspense, streaming SSR. Contact us to evaluate your project — we'll calculate timelines and cost individually. Get a consultation on headless migration — we'll analyze your store and propose an architecture.

Our competencies:

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1393
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1368
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1139
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1152
  • Website development for SBH Partners
    Website development for SBH Partners
    1231
  • Website development for Red Pear
    Website development for Red Pear
    641