Розробка користувацької Shopify додатку
Shopify додаток — це веб-додаток, інтегрований в екосистему Shopify через OAuth 2.0 та API. Користувацька додаток потрібна, коли функціонал з App Store відсутній, не масштабується під завдання або потребує глибокої інтеграції з внутрішніми системами компанії.
Типи Shopify додатків
Public App — публікується в App Store, встановлюється будь-яким магазином. Потребує перевірки від Shopify. Монетизація через підписку.
Custom App — створюється для конкретного магазину в межах його Admin. Не публікується, не потребує перевірки. Встановлюється через пряме посилання або API токен. Оптимальний варіант для корпоративної автоматизації.
Embedded App — відображається всередині Shopify Admin через iframe. Використовує Shopify App Bridge для спілкування з хост-інтерфейсом.
Технологічний стек
Офіційно рекомендований Shopify стек:
- Backend: Node.js (Express) або Ruby on Rails, рідко PHP
- Frontend: React + Shopify Polaris (дизайн-система для Admin UI)
-
App Bridge:
@shopify/app-bridge-react— спілкування з Shopify Admin -
API client:
@shopify/shopify-api(Node) /shopify_api(Ruby gem) - Database: PostgreSQL / MySQL для зберігання даних додатку
- Hosting: Heroku, Railway, Fly.io, власний VPS за nginx
Scaffold через Shopify CLI:
shopify app init my-custom-app
# Вибір: Node.js + React
cd my-custom-app
shopify app dev
OAuth flow та аутентифікація
// web/index.js — Express + @shopify/shopify-api
import { shopifyApp } from '@shopify/shopify-app-express';
import { PostgreSQLSessionStorage } from '@shopify/shopify-app-session-storage-postgresql';
const shopify = shopifyApp({
api: {
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET,
scopes: ['read_products', 'write_products', 'read_orders', 'write_orders'],
hostName: process.env.HOST.replace(/https?:\/\//, ''),
apiVersion: ApiVersion.January25,
},
auth: {
path: '/api/auth',
callbackPath: '/api/auth/callback',
},
webhooks: {
path: '/api/webhooks',
},
sessionStorage: new PostgreSQLSessionStorage(process.env.DATABASE_URL),
});
app.get(shopify.config.auth.path, shopify.auth.begin());
app.get(shopify.config.auth.callbackPath, shopify.auth.callback(), shopify.redirectToShopifyOrAppRoot());
Після OAuth Shopify повертає офлайн-токен (довгоживучий) та онлайн-токен (сесійний). Офлайн зберігається в БД додатку та використовується для фонових завдань.
Admin API: робота з даними магазину
// Отримання замовлень через GraphQL Admin API
const client = new shopify.api.clients.Graphql({ session });
const response = await client.query({
data: `{
orders(first: 50, query: "financial_status:paid created_at:>2025-01-01") {
edges {
node {
id
name
totalPriceSet {
shopMoney { amount currencyCode }
}
lineItems(first: 20) {
edges {
node {
title
quantity
variant {
sku
inventoryQuantity
}
}
}
}
shippingAddress {
city
countryCode
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}`
});
Пагінація через cursor-based (не offset). При >250 об'єктів — ітерація з endCursor.
Webhooks
Webhooks — асинхронне отримання подій від Shopify без polling:
// Реєстрація webhook
shopify.webhooks.addHandlers({
ORDERS_PAID: [{
deliveryMethod: DeliveryMethod.Http,
callbackUrl: '/api/webhooks/orders-paid',
callback: async (topic, shop, body, webhookId) => {
const order = JSON.parse(body);
// Синхронізація з ERP, відправлення в CRM, створення завдання складському системі
await syncOrderToERP(shop, order);
}
}],
PRODUCTS_UPDATE: [{
deliveryMethod: DeliveryMethod.Http,
callbackUrl: '/api/webhooks/products-update',
callback: async (topic, shop, body) => {
const product = JSON.parse(body);
await invalidateProductCache(shop, product.id);
}
}]
});
Shopify вимагає відповіді 200 OK за 5 секунд — важку обробку виносимо в чергу (Bull, BullMQ, Sidekiq).
Polaris UI в Embedded App
// web/frontend/pages/Dashboard.jsx
import {
Page, Layout, Card, DataTable, Badge, Button, Toast
} from '@shopify/polaris';
import { useAppBridge } from '@shopify/app-bridge-react';
import { Redirect } from '@shopify/app-bridge/actions';
export default function Dashboard() {
const app = useAppBridge();
const rows = orders.map(order => [
order.name,
<Badge status={order.financial_status === 'paid' ? 'success' : 'warning'}>
{order.financial_status}
</Badge>,
order.total_price,
<Button onClick={() => {
const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.ADMIN_PATH, `/orders/${order.id}`);
}}>Відкрити</Button>
]);
return (
<Page title="Управління замовленнями" primaryAction={{ content: 'Експорт', onAction: handleExport }}>
<Layout>
<Layout.Section>
<Card>
<DataTable
columnContentTypes={['text', 'text', 'numeric', 'text']}
headings={['Замовлення', 'Статус оплати', 'Сума', 'Дія']}
rows={rows}
/>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
App Extensions
Користувацька додаток може додавати розширення до різних частин Shopify:
- Theme App Extension — блоки в тему (віджети, кнопки, банери)
- Checkout UI Extension — користувацький UI в чекауті (тільки Plus або через Function)
- Admin UI Extension — додаткові блоки на сторінках Admin
- Shopify Functions — серверна бізнес-логіка (скидки, доставка, валідація)
# Додавання Theme App Extension до додатку
shopify app generate extension --template theme_app_extension --name my-widget
Фонові завдання та чергі
// Обробник чергу BullMQ
import { Queue, Worker } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis(process.env.REDIS_URL);
export const syncQueue = new Queue('erp-sync', { connection });
const worker = new Worker('erp-sync', async (job) => {
const { shopDomain, orderId } = job.data;
const session = await loadSessionFromDB(shopDomain);
const client = new shopify.api.clients.Rest({ session });
const order = await client.get({ path: `orders/${orderId}` });
await postToERP(order.body.order);
}, { connection, concurrency: 3 });
worker.on('failed', (job, err) => {
console.error(`Завдання ${job.id} не вдалося:`, err.message);
});
Деплой та інфраструктура
Мінімальна конфігурація для продакшену:
- App server: 1 інстанс (Node.js / Puma), автоперезавантаження через PM2 або systemd
- Worker: окремий процес для чергу
- PostgreSQL: для зберігання сесій та даних додатку
- Redis: для чергу та кешу
- Nginx: reverse proxy + SSL termination
- Webhook endpoint: мають бути доступні публічно (Shopify робить POST)
Терміни
Проста користувацька додаток (CRUD над API, базовий Polaris UI): 1–2 тижні. Додаток з Theme Extension, Webhooks та інтеграцією з зовнішною системою: 3–5 тижнів. Повнофункціональна додаток з Shopify Functions, checkout UI, фоновою синхронізацією та мультимагазинною архітектурою: 2–3 місяці.







