Інтеграція 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-міграції — ми проаналізуємо ваш магазин і запропонуємо архітектуру.







