Интеграция Shopify Storefront API с кастомным фронтендом

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция Shopify Storefront API с кастомным фронтендом
Сложная
~2-4 недели
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Интеграция Shopify Storefront API с кастомным фронтендом

Shopify Storefront API позволяет использовать Shopify как headless commerce backend — управление каталогом, корзиной и чекаутом через GraphQL, при этом фронтенд полностью кастомный: Next.js, Nuxt, Astro, мобильное приложение или любой другой клиент.

Когда нужен headless

Стандартная тема Shopify упирается в ограничения:

  • Структура URL жёстко определена платформой
  • Кастомный чекаут недоступен без Shopify Plus
  • Сложная анимация и нестандартные интерфейсы требуют обходов ограничений Liquid
  • Нужна единая витрина для нескольких Shopify-магазинов
  • PWA или нативное мобильное приложение

Headless решает эти проблемы, но добавляет операционную сложность: собственный хостинг, CI/CD, отдельный деплой фронта.

Storefront API: аутентификация

Для доступа к Storefront API нужен Storefront API access token — публичный токен с ограниченными правами (только чтение каталога и мутации корзины):

Admin > Apps > Develop apps > [App] > Configuration > Storefront API access scopes

Токен передаётся в заголовке X-Shopify-Storefront-Access-Token — он может быть публичным (встроен в JS-код фронтенда).

Базовый клиент

// 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/2025-01/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;
}

Получение каталога

// 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;
}

Управление корзиной

Storefront API использует Cart API (не устаревший Checkout API):

// 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',
  });
}

Корзина хранится на стороне Shopify, ID картри сохраняется в cookie или localStorage клиента. При переходе к оплате используется cart.checkoutUrl — редирект на Shopify-чекаут.

Next.js App Router интеграция

// app/products/[handle]/page.tsx
import { getProduct } from '@/lib/shopify';
import { AddToCartButton } from '@/components/AddToCartButton';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const products = await getProducts({ first: 250 });
  return products.edges.map(({ node }) => ({ handle: node.handle }));
}

export async function generateMetadata({ params }: { params: { handle: string } }) {
  const product = await getProduct(params.handle);
  if (!product) return {};

  return {
    title: product.title,
    description: product.description.slice(0, 160),
    openGraph: {
      images: [{ url: product.featuredImage?.url }],
    },
  };
}

export default async function ProductPage({ params }: { params: { handle: string } }) {
  const product = await getProduct(params.handle);
  if (!product) notFound();

  return (
    <main>
      <h1>{product.title}</h1>
      <AddToCartButton product={product} />
    </main>
  );
}

Инкрементальная статическая регенерация (ISR)

Каталог статически рендерится при сборке, обновляется по вебхуку или по TTL:

// app/api/revalidate/route.ts — вебхук от 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...

  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');
}

Поиск и фильтрация

Storefront API поддерживает query параметр с синтаксисом Shopify search syntax:

// Фильтрация по тегу + цене
const products = await getProducts({
  query: 'tag:sale price:<5000',
  sortKey: 'PRICE',
  reverse: false,
});

// Поиск по названию
const results = await getProducts({
  query: `title:*${searchTerm}*`,
});

Для сложной фильтрации (фасеты по характеристикам) — используется collection.products с фильтрами:

collection(handle: "all") {
  products(first: 24, filters: [
    { price: { min: 1000, max: 5000 } },
    { productMetafield: { namespace: "specifications", key: "material", value: "leather" } },
    { available: true }
  ]) { ... }
}

Интернационализация

Storefront API поддерживает @inContext директиву для локализации цен и контента:

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

Сроки

MVP headless-магазина на Next.js с каталогом, корзиной и чекаутом: 3–4 недели. Полноценный проект с поиском, фильтрацией, ISR, мультиязычностью, аналитикой и интеграцией CMS для контента: 2–3 месяца.