Complete Shopify e-commerce integration that syncs products, orders, customers, inventory, and fulfillment data to PostgreSQL with real-time webhook support.
The Shopify plugin provides complete synchronization between your Shopify store and a local PostgreSQL database. It enables:
# Install the plugin
nself plugin install shopify
# Verify installation
nself plugin status shopify# Required - Shopify store domain
SHOPIFY_SHOP_DOMAIN=your-store.myshopify.com
# Required - Admin API access token
# Create at: Settings > Apps > Develop apps > Create an app
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Required - PostgreSQL connection string
DATABASE_URL=postgresql://user:password@localhost:5432/nself
# Optional - Webhook HMAC secret
SHOPIFY_WEBHOOK_SECRET=your_webhook_secret
# Optional - API version (default: 2024-01)
SHOPIFY_API_VERSION=2024-01
# Optional - Server configuration
PORT=3003
HOST=0.0.0.0read_products - Products and collectionsread_orders - Orders and transactionsread_customers - Customer dataread_inventory - Inventory levelsread_locations - Location dataread_fulfillments - Fulfillment dataread_draft_orders - Draft ordersread_discounts - Discount codesUse a Shopify development store for testing. Development stores are free and have full API access for building and testing integrations.
# Create all required tables
nself-shopify init
# Or via nself CLI
nself plugin shopify init# Sync all data from Shopify
nself-shopify sync
# Sync specific resources
nself-shopify sync --resources products,orders,customers
# Incremental sync (only changes since last sync)
nself-shopify sync --incremental# Start the server
nself-shopify server
# Custom port
nself-shopify server --port 3003
# The server exposes:
# - POST /webhook - Shopify webhook endpoint
# - GET /health - Health check
# - GET /api/* - REST API endpoints# List all products
nself-shopify products list
# Search products
nself-shopify products search "keyword"
# Filter by status
nself-shopify products list --status active
nself-shopify products list --status draft
nself-shopify products list --status archived
# Filter by vendor
nself-shopify products list --vendor "Brand Name"
# Get product details
nself-shopify products get 123456789
# List product variants
nself-shopify products variants 123456789
# Show inventory levels
nself-shopify products inventory 123456789# List all orders
nself-shopify orders list
# Filter by financial status
nself-shopify orders list --status paid
nself-shopify orders list --status pending
nself-shopify orders list --status refunded
# Filter by fulfillment status
nself-shopify orders list --fulfillment unfulfilled
nself-shopify orders list --fulfillment fulfilled
# Filter by date range
nself-shopify orders list --since 2024-01-01 --until 2024-12-31
# Get order details
nself-shopify orders get 123456789
# List order line items
nself-shopify orders items 123456789# List all customers
nself-shopify customers list
# Search customers
nself-shopify customers search "john@example.com"
# Get customer details
nself-shopify customers get 123456789
# View customer orders
nself-shopify customers orders 123456789
# Top customers by total spent
nself-shopify customers top --limit 20# List all inventory
nself-shopify inventory list
# Low stock alerts
nself-shopify inventory low-stock --threshold 10
# Inventory by location
nself-shopify inventory list --location "Main Warehouse"
# Get inventory level for variant
nself-shopify inventory get variant_123456# List fulfillments
nself-shopify fulfillments list
# Filter by status
nself-shopify fulfillments list --status pending
nself-shopify fulfillments list --status success
# Get fulfillment details
nself-shopify fulfillments get 123456789# Show sync status
nself-shopify status
# Output:
# Store: your-store.myshopify.com
# Products: 1,234 (45 variants)
# Orders: 5,678 (open: 23)
# Customers: 3,456
# Inventory Items: 2,345
# Last Sync: 2026-01-24 12:00:00https://your-domain.com/webhooks/shopifyorders/create, orders/updated, orders/paid, orders/fulfilledproducts/create, products/update, products/deletecustomers/create, customers/updateinventory_levels/updateshopify app webhook forward --topic orders/create \
--path /webhooks/shopify --port 443| Resource | Table | Notes |
|---|---|---|
| Shop | shopify_shops | Store settings and metadata |
| Products | shopify_products | Full product catalog |
| Product Variants | shopify_product_variants | Size, color, etc. |
| Product Images | shopify_product_images | All product images |
| Collections | shopify_collections | Smart and custom collections |
| Customers | shopify_customers | Customer profiles |
| Customer Addresses | shopify_customer_addresses | Shipping addresses |
| Orders | shopify_orders | All order data |
| Order Line Items | shopify_order_line_items | Individual line items |
| Fulfillments | shopify_fulfillments | Fulfillment records |
| Refunds | shopify_refunds | Refund records |
| Transactions | shopify_transactions | Payment transactions |
| Inventory Items | shopify_inventory_items | Inventory tracking |
| Inventory Levels | shopify_inventory_levels | Stock quantities |
| Locations | shopify_locations | Warehouse locations |
| Draft Orders | shopify_draft_orders | Unpaid orders |
| Abandoned Checkouts | shopify_abandoned_checkouts | Recovery opportunities |
| Discount Codes | shopify_discount_codes | Promo codes |
| View | Description |
|---|---|
shopify_sales_by_day | Daily sales totals |
shopify_top_products | Best selling products |
shopify_low_stock | Low inventory alerts |
shopify_customer_ltv | Customer lifetime value |
shopify_order_summary | Order status breakdown |
-- Daily sales report
SELECT
DATE(created_at) as date,
COUNT(*) as order_count,
SUM(total_price) as revenue,
AVG(total_price) as avg_order_value
FROM shopify_orders
WHERE financial_status = 'paid'
AND created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date DESC;
-- Top products by revenue
SELECT
p.title,
p.vendor,
COUNT(DISTINCT li.order_id) as order_count,
SUM(li.quantity) as units_sold,
SUM(li.price * li.quantity) as revenue
FROM shopify_products p
JOIN shopify_product_variants v ON p.id = v.product_id
JOIN shopify_order_line_items li ON v.id = li.variant_id
JOIN shopify_orders o ON li.order_id = o.id
WHERE o.financial_status = 'paid'
AND o.created_at > NOW() - INTERVAL '30 days'
GROUP BY p.id, p.title, p.vendor
ORDER BY revenue DESC
LIMIT 20;
-- Customer lifetime value
SELECT
c.id,
c.email,
c.first_name,
c.last_name,
COUNT(DISTINCT o.id) as total_orders,
SUM(o.total_price) as lifetime_value,
MIN(o.created_at) as first_order,
MAX(o.created_at) as last_order
FROM shopify_customers c
JOIN shopify_orders o ON c.id = o.customer_id
WHERE o.financial_status = 'paid'
GROUP BY c.id, c.email, c.first_name, c.last_name
HAVING COUNT(DISTINCT o.id) > 1
ORDER BY lifetime_value DESC
LIMIT 100;
-- Inventory alerts
SELECT
p.title as product,
v.title as variant,
v.sku,
il.available,
l.name as location
FROM shopify_inventory_levels il
JOIN shopify_inventory_items ii ON il.inventory_item_id = ii.id
JOIN shopify_product_variants v ON ii.id = v.inventory_item_id
JOIN shopify_products p ON v.product_id = p.id
JOIN shopify_locations l ON il.location_id = l.id
WHERE il.available <= 10
AND p.status = 'active'
ORDER BY il.available ASC;query GetRecentOrders {
shopify_orders(
order_by: { created_at: desc }
limit: 20
) {
id
order_number
total_price
fulfillment_status
created_at
shopify_customer {
email
first_name
last_name
}
shopify_order_line_items {
title
quantity
price
}
}
}ENV=dev
SHOPIFY_STORE=your-store-dev # Use development store
SHOPIFY_ACCESS_TOKEN=shpat_devUse a Shopify development store for dev/staging.
ENV=prod
SHOPIFY_STORE=your-store # Production store
SHOPIFY_ACCESS_TOKEN=shpat_liveShopify has a 2 requests/second limit for the Admin API. The plugin automatically handles rate limiting with a 0.5s delay between requests.
# Remove plugin and data
nself plugin remove shopify
# Keep database tables
nself plugin remove shopify --keep-dataorders/create - New order placedorders/updated - Order modifiedorders/paid - Order payment receivedorders/fulfilled - Order fulfilledorders/cancelled - Order cancelledproducts/create - New product createdproducts/update - Product modifiedproducts/delete - Product deletedcustomers/create - New customer registeredcustomers/update - Customer modifiedinventory_levels/update - Stock level changedfulfillments/create - Fulfillment createdrefunds/create - Refund issuedcheckouts/create - Checkout startedcheckouts/update - Checkout modifiedhttps://your-domain.com/webhookSHOPIFY_WEBHOOK_SECRETError: [API] Invalid API key or access tokenTest your credentials:
curl -H "X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN" \
"https://$SHOPIFY_SHOP_DOMAIN/admin/api/2024-01/shop.json"Error: Exceeded 2 calls per second for api clientThe plugin handles rate limiting automatically. If you see persistent errors:
Error: Webhook signature verification failedSolutions:
SHOPIFY_WEBHOOK_SECRET matches Shopify adminapplication/jsonLOG_LEVEL=debug nself-shopify sync// Check for low inventory
async function checkLowInventory(threshold: number = 10) {
const result = await hasura.query({
query: `
query LowInventory($threshold: Int!) {
shopify_inventory(
where: { available: { _lte: $threshold } }
) {
product_id
variant_id
available
shopify_variant {
title
sku
shopify_product {
title
}
}
}
}
`,
variables: { threshold }
});
return result.shopify_inventory;
}-- Fulfillment status overview
CREATE VIEW fulfillment_dashboard AS
SELECT
fulfillment_status,
COUNT(*) AS order_count,
SUM(total_price) AS total_value,
AVG(EXTRACT(EPOCH FROM (NOW() - created_at)) / 3600)::int AS avg_hours_pending
FROM shopify_orders
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY fulfillment_status
ORDER BY order_count DESC;query CustomerLTV {
shopify_customer_value(
order_by: { total_spent: desc }
limit: 100
) {
customer_id
email
orders_count
total_spent
average_order_value
first_order_date
last_order_date
}
}Last Updated: January 2026 | Plugin Version 1.0.0 | Shopify API Version 2024-01