Інтеграція 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:

// Фільтрація по тегу + ціні
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 тижні. Повнофункціональний проект з CMS, мультирынком, користувацькою аналітикою, A/B-тестингом та CI/CD: 2–3 місяці.