Workflow: Shopify to Google Sheets Product Sync Automation
Integrations: code, googleSheets, graphql, if, noOp, scheduleTrigger, set, stickyNote, stopAndError, wait

{"id": "WBkJdubQjVzMUhwi", "meta": {"instanceId": "workflow-6c91e266", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:59.573888", "updatedAt": "2025-09-29T07:07:59.573909", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production"}, "name": "Shopify to Google Sheets Product Sync Automation", "tags": ["automation", "n8n", "production-ready", "excellent", "optimized"], "nodes": [{"id": "b2a5a0ac-4ce8-4d81-8d7f-01c0e5e35fd7", "name": "Wait1", "type": "n8n-nodes-base.wait", "position": [1520, 380], "webhookId": "93996a89-7e6c-4f08-9e42-eceb160a7d89", "parameters": {"unit": "seconds", "amount": 10}, "typeVersion": 1, "notes": "This wait node performs automated tasks as part of the workflow."}, {"id": "681361ff-0648-46bd-bff2-2f4c4c17624a", "name": "No Operation, do nothing", "type": "n8n-nodes-base.noOp", "position": [1620, 180], "parameters": {}, "typeVersion": 1, "notes": "This noOp node performs automated tasks as part of the workflow."}, {"id": "1836d799-a821-44c0-b1a7-7d9354afccd4", "name": "Shopify get products", "type": "n8n-nodes-base.graphql", "position": [320, 200], "parameters": {"query": "=query getProducts($first: Int = {{ $json.batchsize }}, $after: String = \"{{ $json.endCursor }}\") {\n  products(first: $first, after: $after) {\n    edges {\n      node {\n        title\n        tags\n        description\n        variants(first: 1) {\n          edges {\n            node {\n              price\n            }\n          }\n        }\n      }\n    }\n    pageInfo {\n      hasNextPage\n      endCursor\n    }\n  }\n}\n", "endpoint": "{{ $env.API_BASE_URL }}", "authentication": "{{ $credentials.headerAuth }}"}, "credentials": {"httpHeaderAuth": {"id": "m0Fan0K6zdS2cpQq", "name": "shopify test store"}}, "executeOnce": true, "typeVersion": 1, "notes": "This graphql node performs automated tasks as part of the workflow."}, {"id": "32a79711-c802-44c8-b188-250a782633c0", "name": "Split output", "type": "n8n-nodes-base.code", "position": [760, 200], "parameters": {"language": "python", "pythonCode": "new_output = []\nfor item in _input.all():\n    products = item.json['data']['products']['edges']\n    for product in products:\n        new_item = {\n            \"data\": {\n                \"product\": product['node']\n            }\n        }\n        new_output.append(new_item)\nreturn new_output"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "c7457a0b-9381-4e96-a458-33bf43f2dce1", "name": "Check if there is next page", "type": "n8n-nodes-base.if", "position": [1300, 200], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "fd562f28-7126-4f06-8250-6b3a4eb4e481", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ $json.data.products.pageInfo.hasNextPage }}", "rightValue": ""}]}}, "typeVersion": 2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "cced491b-b8b5-4109-8bd0-3d51fe0f0b5a", "name": "writing first product details", "type": "n8n-nodes-base.googleSheets", "position": [-140, 380], "parameters": {"columns": {"value": {"tag": "={{ $json.data.products.edges[0].node.tags }}", "price": "={{ $json.data.products.edges[0].node.variants.edges[0].node.price }}", "title": "={{ $json.data.products.edges[0].node.title }}", "descreption": "={{ $json.data.products.edges[0].node.description }}"}, "schema": [{"id": "title", "type": "string", "display": true, "removed": false, "required": false, "displayName": "title", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "descreption", "type": "string", "display": true, "removed": false, "required": false, "displayName": "descreption", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "tag", "type": "string", "display": true, "removed": false, "required": false, "displayName": "tag", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "price", "type": "string", "display": true, "removed": false, "required": false, "displayName": "price", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "defineBelow", "matchingColumns": ["title"]}, "options": {}, "operation": "append", "sheetName": {"__rl": true, "mode": "list", "value": "gid=0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Sheet1"}, "documentId": {"__rl": true, "mode": "list", "value": "1YnGJD7AxV1iiQ-LcxOz3MnTLxGNSC6BBh-2Bh3Yitw0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "template test"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "pmrAlq3hgPc4cCvQ", "name": "Google Sheets account"}}, "executeOnce": true, "typeVersion": 4.2, "alwaysOutputData": false, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "a72b4230-d242-4ffa-a388-fb3580e66300", "name": "Set cursor", "type": "n8n-nodes-base.set", "position": [1420, 740], "parameters": {"fields": {"values": [{"name": "endCursor", "stringValue": "={{ $('Shopify get products').item.json.data.products.pageInfo.endCursor }}"}, {"name": "=batchsize", "stringValue": "={{ $('Code').item.json.batchsize }}"}]}, "include": "none", "options": {}}, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "55a6cb5d-96d0-4577-b74f-d718de9d07cb", "name": "writing remaning product info to google sheets", "type": "n8n-nodes-base.googleSheets", "position": [1020, 200], "parameters": {"columns": {"value": {"tag": "={{ $json.data.product.tags }}", "price": "={{ $json.data.product.variants.edges[0].node.price }}", "title": "={{ $json.data.product.title }}", "descreption": "={{ $json.data.product.description }}"}, "schema": [{"id": "title", "type": "string", "display": true, "removed": false, "required": false, "displayName": "title", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "descreption", "type": "string", "display": true, "removed": false, "required": false, "displayName": "descreption", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "tag", "type": "string", "display": true, "removed": false, "required": false, "displayName": "tag", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "price", "type": "string", "display": true, "removed": false, "required": false, "displayName": "price", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "defineBelow", "matchingColumns": ["title"]}, "options": {}, "operation": "append", "sheetName": {"__rl": true, "mode": "list", "value": "gid=0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Sheet1"}, "documentId": {"__rl": true, "mode": "list", "value": "1YnGJD7AxV1iiQ-LcxOz3MnTLxGNSC6BBh-2Bh3Yitw0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "template test"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "pmrAlq3hgPc4cCvQ", "name": "Google Sheets account"}}, "typeVersion": 4.2, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "a24c4e2a-482f-43d4-8c48-927427a430c0", "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "position": [-1300, 520], "parameters": {"rule": {"interval": [{"daysInterval": 0, "triggerAtHour": 7}]}}, "typeVersion": 1.1, "notes": "This scheduleTrigger node performs automated tasks as part of the workflow."}, {"id": "3a9d27fa-0840-4fc1-9b67-aad2f89f479b", "name": "update Curser", "type": "n8n-nodes-base.googleSheets", "position": [640, 0], "parameters": {"columns": {"value": {"tracker": "cursor", "endCursor": "={{ $json.data.products.pageInfo.endCursor }}"}, "schema": [{"id": "tracker", "type": "string", "display": true, "removed": false, "required": false, "displayName": "tracker", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "endCursor", "type": "string", "display": true, "removed": false, "required": false, "displayName": "endCursor", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "row_number", "type": "string", "display": true, "removed": false, "readOnly": true, "required": false, "displayName": "row_number", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "defineBelow", "matchingColumns": ["tracker"]}, "options": {}, "operation": "update", "sheetName": {"__rl": true, "mode": "list", "value": 334929034, "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Curser"}, "documentId": {"__rl": true, "mode": "list", "value": "1YnGJD7AxV1iiQ-LcxOz3MnTLxGNSC6BBh-2Bh3Yitw0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Shopify Product Sync test"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "pmrAlq3hgPc4cCvQ", "name": "Google Sheets account"}}, "executeOnce": false, "typeVersion": 4.2, "alwaysOutputData": false, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "a7c1f97c-d88f-457d-9213-36300d277f4b", "name": "If Node", "type": "n8n-nodes-base.if", "position": [-540, 520], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "32b5f953-ae6c-4c50-ac47-591880738d0f", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.endCursor }}", "rightValue": ""}]}}, "typeVersion": 2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "23f62f9c-ef85-4e25-9d94-83a1d899ecf8", "name": "Code", "type": "n8n-nodes-base.code", "position": [100, 540], "parameters": {"jsCode": "let mergedJson = {};\n\ntry {\n    const batch_size = $(\"BatchSize\").all(0, 0);\n    if (batch_size.length > 0 && batch_size[0].json) {\n        Object.assign(mergedJson, batch_size[0].json);\n    }\n} catch (error) {\n    console.log(\"BatchSize data not available\");\n}\n\nlet endCursorFound = false;\ntry {\n    const last_cursor = $(\"LastCursor\").all(0, 0);\n    if (last_cursor.length > 0 && last_cursor[0].json) {\n        Object.assign(mergedJson, last_cursor[0].json);\n        if (last_cursor[0].json.endCursor) {\n            mergedJson.endCursor = last_cursor[0].json.endCursor;\n            endCursorFound = true;\n        }\n    }\n} catch (error) {\n    console.log(\"LastCursor data not available\");\n}\n\nif (!endCursorFound) {\n    try {\n        const shopify_initial = $(\"shopify-initial\").all(0, 0);\n        if (shopify_initial.length > 0 && shopify_initial[0].json && shopify_initial[0].json.data && shopify_initial[0].json.data.products && shopify_initial[0].json.data.products.pageInfo) {\n            mergedJson.endCursor = shopify_initial[0].json.data.products.pageInfo.endCursor;\n        }\n    } catch (error) {\n        console.log(\"Shopify data not available\");\n    }\n}\n\nif (Object.keys(mergedJson).length === 0 || mergedJson.hasOwnProperty('error')) {\n    return [{ json: { error: \"No data available. Ensure relevant nodes have been executed.\" } }];\n}\n\nreturn [{ json: mergedJson }];"}, "executeOnce": true, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "f1262f15-757f-4cc2-9453-fed17ad66b56", "name": "BatchSize", "type": "n8n-nodes-base.set", "position": [-1080, 520], "parameters": {"fields": {"values": [{"name": "batchsize", "type": "numberValue", "numberValue": "100"}]}, "include": "selected", "options": {}}, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "e885b0e7-e435-40ae-be21-77fd992c3114", "name": "LastCursor", "type": "n8n-nodes-base.googleSheets", "position": [-720, 520], "parameters": {"options": {}, "sheetName": {"__rl": true, "mode": "list", "value": 334929034, "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Curser"}, "documentId": {"__rl": true, "mode": "list", "value": "1YnGJD7AxV1iiQ-LcxOz3MnTLxGNSC6BBh-2Bh3Yitw0", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Shopify Product Sync test"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "pmrAlq3hgPc4cCvQ", "name": "Google Sheets account"}}, "typeVersion": 4.2, "alwaysOutputData": true, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "ae3cf866-8695-4b63-b631-a6b00e29c7cb", "name": "shopify-initial", "type": "n8n-nodes-base.graphql", "position": [-300, 380], "parameters": {"query": "=query getProducts($first: Int = 1) {\n  products(first: $first) {\n    edges {\n      node {\n        title\n        tags\n        description\n        variants(first: 1) {\n          edges {\n            node {\n              price\n            }\n          }\n        }\n      }\n    }\n    pageInfo {\n      hasNextPage\n      endCursor\n    }\n  }\n}\n", "endpoint": "{{ $env.API_BASE_URL }}", "authentication": "{{ $credentials.headerAuth }}"}, "credentials": {"httpHeaderAuth": {"id": "m0Fan0K6zdS2cpQq", "name": "shopify test store"}}, "typeVersion": 1, "notes": "This graphql node performs automated tasks as part of the workflow."}, {"id": "8aab80ca-1a54-4d02-a8e8-37d037a12132", "name": "Check cursor is not empty", "type": "n8n-nodes-base.if", "position": [420, 20], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "329a4250-3fe7-4c73-8918-d41f7b38ff5a", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json.data.products.pageInfo.endCursor }}", "rightValue": ""}]}}, "typeVersion": 2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "9e7c2e36-71f6-4fdf-a3b9-8aa3bf02d09b", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [-1500, -400], "parameters": {"color": 4, "width": 352.8896103896103, "height": 295.09740259740255, "content": "This workflow automates the synchronization of product data from a Shopify store to a Google Sheets document, ensuring seamless management and tracking. It retrieves product details such as title, tags, description, and price from Shopify via GraphQL queries. The outcome is a comprehensive list of products neatly organized in Google Sheets for easy access and analysis."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "fbf62e09-3598-4f5c-b83a-a8b3e5371afb", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [-1420, 340], "parameters": {"width": 262.2077922077919, "height": 343.21428571428567, "content": "Schedule Trigger: Sets the timing for the automation to run, ensuring regular updates. Currently set to trigger every day at 7:00 AM"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "47abe6ba-a7de-410e-b634-8ad248ec7155", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [-1140, 360], "parameters": {"color": 3, "width": 275.1623376623376, "height": 411.6883116883117, "content": "BatchSize: Defines the number of products to fetch from Shopify at a time, optimizing data retrieval. Currently set to 100, but it can be adjusted to a maximum of 250 for a single run"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "6415976b-5fa5-4cd4-aa86-58eb9749a878", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [-820, 260], "parameters": {"color": 5, "width": 275.16233766233773, "height": 419.0909090909093, "content": "LastCursor: Checks if the last cursor data is already present in Google Sheets to facilitate incremental data fetching. This ensures that the synchronization process does not start from the beginning each time, optimizing efficiency by picking up where it left off"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "6a15e240-111e-4c7d-a865-2484a7a6ff0c", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [-380, -160], "parameters": {"color": 4, "width": 450.9740259740258, "height": 705.941558441558, "content": "Shopify-initial: Fetches the initial set of products from the Shopify store to start the synchronization process. This node will only run once if there is no cursor found in the previous node, which retrieves the cursor and the first set of products"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "71640487-d3cf-4ede-8677-093108770720", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [-160, 560], "parameters": {"color": 6, "width": 416.49350649350646, "height": 402.4350649350655, "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nThis code node merges data from different sources (BatchSize, LastCursor, and Shopify-initial) to ensure the synchronization process starts efficiently and picks up where it left off. It checks for available data and retrieves the last cursor position from Google Sheets to facilitate incremental data fetching."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a13069b8-36f9-4604-895e-55c51ae3be2c", "name": "Sticky Note6", "type": "n8n-nodes-base.stickyNote", "position": [660, 200], "parameters": {"width": 304.7727272727272, "height": 330.2597402597403, "content": "\n\n\n\n\n\n\n\n\n\nThe \"Split output\" node acts as a bridge between data retrieval and subsequent processing nodes. Since the Shopify node fetches batches of 100 results at a time, this node splits those batches into individual product entries, ensuring seamless processing and storage of each product's details in subsequent workflow steps"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "8c1401ad-e7be-47a9-b01d-3606b9f20bf0", "name": "Sticky Note7", "type": "n8n-nodes-base.stickyNote", "position": [1400, 620], "parameters": {"color": 5, "width": 388.0519480519479, "height": 367.27272727272714, "content": "Set cursor: Updates the cursor for the next page of products to fetch from Shopify."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a5d3c62c-1bf3-4bc7-9e2b-1b5883b385d1", "name": "Sticky Note8", "type": "n8n-nodes-base.stickyNote", "position": [-32.17532467532425, 20], "parameters": {"color": 3, "width": 428.7662337662332, "height": 342.79220779220765, "content": "The GraphQL query within this node is crafted to extract essential product details such as title, description, tags, and price. This query can be customized to fetch additional product information as needed for specific synchronization requirements."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "error-64a3aad7", "name": "Error Handler", "type": "n8n-nodes-base.stopAndError", "typeVersion": 1, "position": [1000, 400], "parameters": {"message": "Workflow execution error", "options": {}}}], "active": false, "pinData": {}, "settings": {"executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000}, "versionId": "c640732c-55b5-4f2e-bb64-106c440b0abc", "connections": {"cced491b-b8b5-4109-8bd0-3d51fe0f0b5a": {"main": [[{"node": "error-handler-cced491b-b8b5-4109-8bd0-3d51fe0f0b5a-9c19caa7", "type": "main", "index": 0}]]}, "55a6cb5d-96d0-4577-b74f-d718de9d07cb": {"main": [[{"node": "error-handler-55a6cb5d-96d0-4577-b74f-d718de9d07cb-04ec7734", "type": "main", "index": 0}]]}, "3a9d27fa-0840-4fc1-9b67-aad2f89f479b": {"main": [[{"node": "error-handler-3a9d27fa-0840-4fc1-9b67-aad2f89f479b-2d35c521", "type": "main", "index": 0}]]}, "e885b0e7-e435-40ae-be21-77fd992c3114": {"main": [[{"node": "error-handler-e885b0e7-e435-40ae-be21-77fd992c3114-faa1e134", "type": "main", "index": 0}]]}}, "description": "Automated workflow: Shopify to Google Sheets Product Sync Automation. This workflow integrates 10 different services: stickyNote, wait, code, scheduleTrigger, graphql. It contains 29 nodes and follows best practices for error handling and security.", "notes": "Excellent quality workflow: Shopify to Google Sheets Product Sync Automation. This workflow has been optimized for production use with comprehensive error handling, security, and documentation."}