How to Update Shopify Inventory from a MySQL Database
Use your MySQL database as the inventory source of truth and push levels to Shopify.
What This Integration Does
If your warehouse management system, ERP, or a custom in-house tool owns inventory, Shopify needs to mirror it - not the other way around. This workflow runs on a tight schedule, queries MySQL for SKUs whose quantity has changed since the last sync, and pushes the new levels into Shopify. The result: stock in your storefront stays accurate without anyone updating Shopify by hand.
The pipeline is incremental: only SKUs that changed since the last run get pushed, so a typical run touches a few dozen items rather than the whole catalog. Each push is keyed by SKU and uses Shopify's inventory adjustment endpoint, so re-running the same window is safe.
Prerequisites
- A MySQL connection with read access to the inventory table (must include
sku,quantity, andupdated_at). - An index on
updated_atin MySQL so incremental queries stay fast. - A Shopify connection with the
write_inventoryscope. - SKUs in MySQL that exactly match SKUs configured on Shopify variants. Mismatches are the single biggest source of "missing" updates.
- The Shopify location ID where inventory is tracked (most stores have one default location, but multi-location stores need an explicit ID).
Step 1: Schedule Trigger
Drop a Trigger node and set it to Schedule. Every 5 to 15 minutes works for most stores - tight enough that overselling is rare, loose enough that you don't blow through Shopify's rate budget on a quiet day. The trigger exposes the previous run timestamp.
Step 2: Variable - Compute the Sync Window
Add a Variable assignment for the window:
syncStart- the previous run's timestamp, or 1 hour ago on the first run.syncEnd- the current timestamp.
Step 3: Query MySQL for Changed SKUs
Add a Connector node for the mysql connector with the execute-query tool:
SELECT sku, quantity
FROM inventory
WHERE updated_at >= '{{ syncStart }}'
AND updated_at < '{{ syncEnd }}'
ORDER BY sku
If your inventory table has multiple warehouses, also join or filter on warehouse so each Shopify location gets the right slice.
Step 4: Get Shopify Inventory Item IDs
Shopify's inventory tools key off inventory_item_id, not sku. Add a Connector node calling the shopify get-inventory-levels tool, scoped to the SKUs in your query result (or to the location and let pagination fetch them all on first run, then cache). Build a lookup table mapping sku to inventory_item_id with a Transform node. For a stable catalog, cache that mapping in a mongodb collection so you don't re-fetch it every run.
Step 5: Loop and Adjust
Wrap the next step in a Loop over the rows from Step 3. For each row, resolve the inventory_item_id from the Step 4 lookup, then call the shopify adjust-inventory tool:
- inventory_item_id: from the lookup
- location_id: your Shopify location
- available:
{{ row.quantity }}- this is the absolute new level, not a delta
Add a Condition branch first that skips the call if the SKU isn't in the Shopify lookup - those are MySQL SKUs that don't exist in Shopify and need to be reported, not pushed.
Step 6: Report and Alert
After the loop, summarize: total SKUs processed, count updated, count skipped (no Shopify match), count failed. Route to a slack send-message step. If the skipped count is non-zero, include the SKU list so someone can fix the mismatches. Spojit's per-node retry handles transient Shopify 429s automatically; a sustained failure (5 retries) routes to the same Slack channel as a separate alert.
Tips
- Use the absolute level, not a delta -
adjust-inventorywith an absolute level is idempotent. Deltas double-count if a run is retried. - Cache the SKU lookup - the SKU to
inventory_item_idmap only changes when products are added. Refresh it once an hour in a separate workflow, not every inventory sync. - Concurrency cap - set the loop's concurrency to 2 or 3. Shopify's per-store rate limit is generous but easy to blow with 100 parallel inventory writes.
Common Pitfalls
- SKU mismatches -
SKU-001in MySQL vssku-001in Shopify is the most common failure mode. Either normalize at the source or do a case-insensitive match in the lookup transform. - Negative quantities - some inventory systems use negative numbers to represent oversold stock. Shopify accepts negatives via
adjust-inventorybut most reports expect zero. Decide a policy and clamp in Step 5. - Multi-location stores - if you sell from multiple warehouses, each MySQL row needs a
locationcolumn and the workflow needs one Shopify call per (sku, location) pair. Don't just dump everything to the default location. - Backorder confusion - if Shopify is set to "continue selling when out of stock", a 0 quantity isn't the same as "stop selling". Coordinate with the store config so the sync intent is clear.
Testing
Pick three test SKUs in MySQL, change their quantities, and run the workflow manually. Confirm Shopify shows the new levels and the Slack summary matches. Run it again immediately and confirm zero rows update (since nothing changed) - this proves the incremental window is working. Then turn the schedule on and watch the first few runs.