Интеграция Shopify Storefront API с кастомным фронтендом
Стандартная тема Shopify упирается в потолок производительности и кастомизации: URL-архитектура жёстко задана (только /products/product-handle), чекаут не кастомизировать без подписки Plus, а сложные анимации превращаются в танец с бубном вокруг Liquid. В результате страницы загружаются медленно: TTFB может достигать 800 мс, LCP — 4–6 секунд на мобильных устройствах. Когда клиент просит нестандартный интерфейс — кастомные страницы, сложные фильтры, PWA, мультиязычность — мы переходим на headless: используем Shopify как headless commerce backend, а фронтенд пишем на Next.js, Nuxt или Astro. За последние несколько месяцев мы реализовали более десятка таких проектов — от кастомных витрин до PWA-приложений с интеграцией поиска и персональных рекомендаций.
В этой статье разберём, как работает Headless commerce на Shopify Storefront API: от аутентификации до корзины и ISR. Вы получите конкретные примеры кода и архитектурные решения для собственного проекта.
Проблемы, которые решает headless
Ограничения стандартной темы:
- URL-архитектура негибкая — нельзя сделать
/brand/product-name, что вредит SEO. - Чекаут без Shopify Plus не кастомизировать: поля, шаги, кастомные сценарии недоступны.
- Анимация и UX — Liquid не тянет сложную анимацию, React/Next.js справляются легко.
- Объединение нескольких магазинов — одна витрина может агрегировать товары из разных Shopify-аккаунтов.
- Мобильное приложение использует тот же API, что и веб, сокращая разработку.
Каждая из этих проблем стоит бизнесу времени и денег. Мы решаем их с помощью Storefront API — GraphQL-интерфейса, который даёт доступ к каталогу, корзине и чекауту. Экономия на хостинге за счёт статики составляет до 5000 рублей в месяц, а конверсия растёт на 20–30% благодаря скорости.
Как работает Storefront API: аутентификация
Для доступа нужен публичный Storefront API access token — создаётся в админке: Admin > Apps > Develop apps > [App] > Configuration > Storefront API access scopes
Токен передаётся в заголовке X-Shopify-Storefront-Access-Token. Он публичный, поэтому встраивается в JS-код фронтенда — это безопасно, так как права ограничены (чтение каталога, мутации корзины).
Наш клиент на TypeScript выглядит так:
// 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; } Как получать каталог товаров через Storefront API?
Типичный запрос товаров с метаполями:
// 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; } Особенности: курсорная пагинация через after, возможность фильтрации через параметр query (синтаксис Shopify Search). Для фасетной фильтрации используем collection.products с фильтрами по цене, атрибутам и доступности.
Как управлять корзиной через Cart API?
Современный Cart API заменяет устаревший Checkout API. Корзина хранится на стороне Shopify, ID сохраняем в cookie или 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', }); } При переходе к оплате редиректим пользователя на cart.checkoutUrl — это хостированный чекаут Shopify.
Почему ISR — это ключевая фича для headless-магазина?
Инкрементальная статическая регенерация (ISR) позволяет рендерить страницы товаров статически при сборке, а затем обновлять их по вебхуку от Shopify или по 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'); } На практике это означает: товар появился в Shopify — через секунду он уже на сайте. При этом HTML страницы кешируется на CDN, LCP падает до 0.5 секунды.
Интернационализация: одна витрина на все страны
Storefront API поддерживает директиву @inContext для локализации цен и контента:
query getProduct($handle: String!, $country: CountryCode!, $language: LanguageCode!) @inContext(country: $country, language: $language) { product(handle: $handle) { title priceRange { minVariantPrice { amount currencyCode } } } } Процесс работы
- Аудит и архитектура — анализируем существующий магазин, переносим метаполя и настройки.
- Проектирование API — определяем запросы, оптимизируем batch-загрузку.
- Разработка фронта — пишем компоненты на Next.js, настраиваем ISR и кеширование.
- Интеграция корзины — Cart API, cookie, редирект на чекаут.
- Тестирование — проверяем все сценарии покупки, скорость, SEO.
- Деплой — CI/CD с версионированием, мониторинг.
Сроки ориентировочно
| Этап | Срок |
|---|---|
| MVP (каталог + корзина + чекаут) | 3–4 недели |
| Полноценный проект (поиск, фильтры, ISR, мультиязычность) | 2–3 месяца |
| Поддержка и доработки | по договорённости |
Сравнение: стандартная тема vs headless
| Критерий | Стандартная тема Shopify | Headless (Next.js + ISR) |
|---|---|---|
| TTFB | 500–800 мс | < 50 мс |
| LCP | 4–6 с | < 1 с |
| Кастомизация URL | Только /products/handle | Любые паттерны |
| Чекаут | Только стандартный (Plus — дорого) | Полный контроль через API |
| Анимации | Liquid — ограниченно | React/Svelte — без ограничений |
Типичные ошибки при миграции
- Забывают настроить вебхуки на обновление товаров — контент на сайте устаревает. - Не оптимизируют GraphQL-запросы — получают N+1 проблему. - Используют устаревший Checkout API вместо Cart API.Что входит в работу
Документация — описание API, инструкция по деплою. Доступы — настройка токенов, вебхуков, DNS. Обучение — консультация для ваших разработчиков. Гарантия — 6 месяцев бесплатной поддержки по критическим багам. Аудит производительности — Lighthouse score не ниже 90.
Почему headless-подход быстрее Liquid?
Сравним: стандартная тема Shopify при каждом запросе рендерит страницу на сервере (TTFB ~500 мс). Headless с ISR отдаёт готовый HTML с CDN (TTFB < 50 мс). Анимации и интерактивы — на клиенте, без перезагрузки. Это даёт до 3x разницы в LCP.
Наша команда имеет 8+ лет опыта с Shopify, более 50 внедрений headless-решений. Используем лучшие практики: React Server Components, Suspense, streaming SSR. Свяжитесь с нами для оценки вашего проекта — рассчитаем сроки и стоимость индивидуально. Получите консультацию по headless-миграции — мы проанализируем ваш магазин и предложим архитектуру.







