Интеграция 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-миграции — мы проанализируем ваш магазин и предложим архитектуру.







