Headless Shopify на Next.js - де-факто стандарт для высокопроизводительного eCommerce в 2026 году. Это не упрощение по сравнению с Liquid-темами - это переработка трёх уровней, которые большинство туториалов обходят стороной: API-поверхности Shopify - все три уровня (Storefront API, Admin API, Customer Account API) с их моделями аутентификации, rate limiting и пагинацией; data layer Next.js App Router - как RSC, Data Cache и generateStaticParams убирают клиентские waterfalls для каталога; и модели state-границ - что идёт в Server Components, что требует 'use client', и точные механизмы (cookies для cart ID, OAuth PKCE, оптимистичные обновления) интерактивного слоя. Всё основано на реальных продакшн-реализациях включая Shopify Plus с несколькими сторами.
Часть I - API-поверхность Shopify: три уровня, три разных контракта
Наиболее распространённая архитектурная ошибка - воспринимать Storefront API как 'API Shopify', игнорируя Admin API и Customer Account API.
- Storefront API (GraphQL, публичный): Эндпоинт
https://{shop}.myshopify.com/api/{version}/graphql.json. Аутентификация черезX-Shopify-Storefront-Access-Token. Rate limiting cost-based: 1,000 cost units/запрос, refill 500 units/секунду. Пагинация - только cursor-based (first: N, after: $cursor), не offset-based. API версионирован (квартальные релизы:2025-01,2025-04). Версия должна быть явно зафиксирована - никогда не используйтеunstableв продакшне. - Admin API (GraphQL + REST, приватный): Полное управление магазином: инвентарь, заказы, данные клиентов, метаполя, вебхуки. Аутентификация через
X-Shopify-Access-Token. Никогда не открывайте Admin API credentials браузеру. Admin API принадлежит исключительно Route Handlers или серверным утилитам сimport 'server-only'. - Customer Account API (OAuth 2.0, 2024+): Shopify отказался от legacy Storefront API мутаций для клиентов (
customerCreate,customerAccessTokenCreate) в 2024. Замена - Customer Account API: отдельный OAuth 2.0 сервис наshopify.com/authentication/{shop-id}. Использует PKCE flow (RFC 7636), выдаёт короткоживущие access tokens (1 час) + refresh tokens. Любая новая headless-реализация в 2026 году обязана использовать этот API.
Часть II - Data Layer: типизированный GraphQL с кодогенерацией
- Инструментарий:
@graphql-codegen/cli+@graphql-codegen/typescript-operations+@shopify/api-codegen-preset. Пресет скачивает схему Storefront API для зафиксированной версии и генерирует TypeScript-типы для всех.graphql-операций. Запускать в CI - типы всегда синхронизированы со схемой. - Стратегия фрагментов: Определить переиспользуемые фрагменты для форм данных компонентов:
fragment ProductCardFields on Product { id handle title featuredImage { url altText } priceRange { minVariantPrice { amount currencyCode } } }. Запросы PDP и PLP составляют фрагменты - без расхождения форм данных между страницами. - Серверный data layer: Все Shopify-функции в
lib/shopify/сimport 'server-only'в индексном файле. TypeScript выдаст ошибку сборки при попытке импортировать серверный код в Client Component.process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN- переменная без префиксаNEXT_PUBLIC_, никогда не попадает в клиентский бандл.
Часть III - Архитектура каталога: generateStaticParams и ISR
Каталог (PDP, PLP) - наиболее критичная по производительности поверхность eCommerce-стора. Правильная архитектура: пре-рендер всех страниц каталога при сборке через generateStaticParams с ISR для актуальности данных. Это устраняет серверный round-trip на каждый запрос и обеспечивает LCP < 1.2s p75 для запросов из CDN-кэша.
- PDP generateStaticParams:
export async function generateStaticParams() { const products = await getAllProductHandles(); return products.map((handle) => ({ handle })); }.getAllProductHandles()пагинирует Storefront API cursor-based: 5,000 продуктов = 20 запросов при сборке, нуль запросов при обслуживании страницы. - ISR конфигурация:
export const revalidate = 3600;- регенерация в фоне каждый час. Для inventory-critical страниц:revalidate = 300+ on-demand через вебхук Shopify → Route Handler →revalidateTag('product-' + handle). - RSC-паттерн для PDP: Страница -
asyncServer Component.const product = await getProduct(params.handle);- данные загружаются напрямую в теле компонента. Заголовок продукта, описание, изображения, характеристики = Server Components, нуль байт в клиентский бандл.<AddToCartButton>и<VariantSelector>- единственные'use client'компоненты. - Пагинация коллекций: Cursor-based:
products(first: 24, after: $cursor). Каждая 'страница' коллекции - отдельный статический маршрут (/collections/[handle]/page/[page]). Кэшируем на CDN. Offset-based (?page=2) не подходит, если порядок продуктов динамический.
Часть IV - Состояние корзины: cookie-хранилище и оптимистичные обновления
- Cart API vs Checkout API: Используйте Cart API (текущий стандарт). Мутации:
cartCreate(создание),cartLinesAdd,cartLinesUpdate,cartLinesRemove,cartBuyerIdentityUpdate(привязка клиента после авторизации). - Cookie-хранилище cart ID:
localStorageнедоступен в Server Components.cartCreateвозвращаетcart.id- сохранять в cookie:httpOnly: false(клиентский JS должен его читать),sameSite: 'lax',path: '/',maxAge: 30 * 24 * 60 * 60. В Server Component читается черезcookies()для рендеринга количества товаров в шапке без клиентского fetch. - Граница `'use client'`:
<CartProvider>- Client Component с React Context. Мутации корзины проксируются через Route Handlers (/api/cart/lines/add) - токен Storefront API никогда не попадает в браузер. - Оптимистичные обновления (React 19):
useOptimisticдаёт мгновенный UI-фидбек до подтверждения от сервера. При ошибке мутации React автоматически возвращает серверное состояние. Устраняет 200–400ms spinner на кнопке 'Добавить в корзину'.
Часть V - Аутентификация: OAuth PKCE через Customer Account API
- PKCE flow (RFC 7636): (1) Генерация
code_verifier(43–128 символов). (2) Вычислениеcode_challenge = BASE64URL(SHA-256(code_verifier)). (3) Редирект наshopify.com/authentication/{shop-id}/oauth/authorizeс параметрами PKCE. (4) Обменcode+code_verifierна access token в Route Handler.code_verifierхранится вhttpOnlycookie - недоступен клиентскому JS. - Auth.js v5 adapter: Customer Account API конфигурируется как custom provider в Auth.js v5. Auth.js управляет обновлением токена, сериализацией сессии и жизненным циклом PKCE verifier.
- `cartBuyerIdentityUpdate` после логина: Связывает анонимную корзину гостя с аккаунтом клиента. Открывает историю заказов, сохранённые адреса, начисление баллов лояльности за предыдущую анонимную активность.
Часть VI - SEO: product schema и каноникал вариантов
- Product structured data: Schema
ProductсOffer(цена, валюта, доступность изproduct.variants.availableForSale),AggregateRating(если есть интеграция с отзывами),BreadcrumbList.Product.url- всегда canonical URL без параметра?variant=. - Каноникал URL вариантов: Shopify добавляет
?variant={id}к URL при выборе варианта. ВgenerateMetadata()всегда возвращатьalternates: { canonical: '/products/' + handle }без параметра варианта. Консолидирует все сигналы PageRank на базовый URL продукта. - hreflang для мультирегиональных сторов: Shopify Markets + Next.js i18n +
generateMetadata().alternates.languages. Подробный анализ реализации - в гайде по App Router миграции.
Часть VII - Производительность: бандл и image pipeline
- `'use client'` поверхность на PDP: Только
<VariantSelector>+<AddToCartButton>+ опционально<ImageGallery>(~15–25 KB gzip). Заголовок, описание, характеристики, отзывы, похожие продукты - Server Components. Итоговый бандл: 80–120 KB gzip против 350–500 KB у Liquid + Alpine.js. Детальный анализ связи сокращения бандла с INP - в The Universal Web Performance Architecture. - Shopify CDN image трансформации:
cdn.shopify.comподдерживает трансформации через URL-параметры:?width=800&height=800&crop=center&format=avif&quality=80. Отдельный image CDN не нужен.next/image+remotePatternsдляcdn.shopify.com. - Preconnect:
<link rel='preconnect' href='https://cdn.shopify.com' />вapp/layout.tsx. Устраняет ~150–200ms задержки DNS + TLS на первый Shopify CDN-запрос. - Edge caching:
Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400для статических страниц. Route Handlers корзины (/api/cart/*) -Cache-Control: no-storeобязательно.
Часть VIII - Продакшн-паттерны: референсы кейсов
- Global Home & Decor eCommerce ([кейсы](/ru/projects)): Multi-storefront Shopify Plus + Magento 2 для B2B и B2C-аудиторий на 12 рынках. Единый типизированный интерфейс
Productдля обоих бэкендов - одни React-компоненты для Shopify и Magento. Паттерн composable headless architecture в наиболее требовательной форме. - High-Load Retail (Traffic Spikes):
generateStaticParamsпре-рендерит весь PDP/PLP при сборке - пиковый трафик поглощается CDN edge-нодами без origin-запросов к Shopify API. Во время спайков на сервер приходят только мутации корзины. Для углублённых паттернов enterprise eCommerce - сервис enterprise ecommerce. - Shopify Hydrogen vs. Next.js: Hydrogen подходит, когда Shopify - единственный бэкенд, команда знакома с Remix, и деплой на Oxygen. Next.js - при нескольких бэкендах, существующей React/Next.js кодовой базе или несовместимости с Oxygen. Полное сравнение - Shopify Hydrogen vs Next.js Commerce.
FAQ
- Почему нельзя хранить ID корзины в localStorage? localStorage недоступен в Server Components и при SSR. Количество товаров в шапке должно рендериться на сервере - это часть начального HTML. Cookie с
httpOnly: falseиsameSite: 'lax'- единственный механизм, доступный и серверу (cookies()), и клиенту. - Как обработать rate limiting Storefront API при большом каталоге? Паттерн ISR означает нуль Storefront API-вызовов на закэшированные страницы. Rate limiting актуален только при сборке (
generateStaticParams) и фоновой ISR-регенерации - оба случая имеют низкую частоту. При сборке каталога из 5,000+ продуктов используйте билд-время data pipeline с явным throttling. - Как реализовать on-demand ISR при обновлении продукта в Shopify? Вебхук Shopify
products/update→ Route Handler с валидацией HMAC-подписи →revalidateTag('product-' + handle). Следующий запрос к PDP триггерит регенерацию. - Как правильно обрабатывать `?variant=` URL для SEO? В
generateMetadata()всегда возвращатьalternates: { canonical: '/products/' + handle }без параметра варианта. Параметр?variant=можно оставить в URL для shareability - Google соблюдает canonical и консолидирует сигналы на базовый URL. Никогда не включать?variant=URL в sitemap.
Источники
- Shopify. 'Storefront API Reference': https://shopify.dev/docs/api/storefront
- Shopify. 'Customer Account API': https://shopify.dev/docs/api/customer
- Shopify. 'Shopify API Rate Limits': https://shopify.dev/docs/api/usage/rate-limits
- RFC 7636. 'Proof Key for Code Exchange': https://datatracker.ietf.org/doc/html/rfc7636
- Next.js Team. 'generateStaticParams': https://nextjs.org/docs/app/api-reference/functions/generate-static-params
- Next.js Team. 'Incremental Static Regeneration': https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration
- GraphQL Code Generator. 'TypeScript Operations': https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-operations
- Auth.js. 'Custom OAuth Providers': https://authjs.dev/guides/configuring-oauth-providers
- HTTP Archive. 'Web Almanac 2025 - eCommerce': https://almanac.httparchive.org
